summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Liao <rogerliao@google.com>2020-06-11 11:03:19 +0800
committerRoger Liao <rogerliao@google.com>2020-06-11 11:03:21 +0800
commit5bf0a2155a7968e6ee2eb93c2162598005f4700c (patch)
tree81362f411a98863972ed6f1434e7bf2d76a90d92
parent3f9e26e5f9a67b62fd51bd0f5e16e410b3be86e4 (diff)
parent3cda844bcb2212629d34872fe5fc5fd0e6e8194f (diff)
downloadfts_touch-5bf0a2155a7968e6ee2eb93c2162598005f4700c.tar.gz
Merge branch 'android-msm-pixel-4.19' into android-msm-barbet-4.19
From build 6575781 Bug: 158714637 Signed-off-by: Roger Liao <rogerliao@google.com> Change-Id: I37e2dad02953b229bbd637af7bc0df528da51504
-rw-r--r--Kbuild8
-rw-r--r--Kconfig13
-rw-r--r--Makefile8
-rw-r--r--fts.c6300
-rw-r--r--fts.h531
-rw-r--r--fts_lib/ftsCompensation.c1123
-rw-r--r--fts_lib/ftsCompensation.h181
-rw-r--r--fts_lib/ftsCore.c1243
-rw-r--r--fts_lib/ftsCore.h216
-rw-r--r--fts_lib/ftsError.c349
-rw-r--r--fts_lib/ftsError.h236
-rw-r--r--fts_lib/ftsFlash.c1123
-rw-r--r--fts_lib/ftsFlash.h140
-rw-r--r--fts_lib/ftsFrame.c667
-rw-r--r--fts_lib/ftsFrame.h118
-rw-r--r--fts_lib/ftsGesture.c377
-rw-r--r--fts_lib/ftsGesture.h47
-rw-r--r--fts_lib/ftsHardware.h319
-rw-r--r--fts_lib/ftsIO.c924
-rw-r--r--fts_lib/ftsIO.h80
-rw-r--r--fts_lib/ftsSoftware.h557
-rw-r--r--fts_lib/ftsTest.c7770
-rw-r--r--fts_lib/ftsTest.h500
-rw-r--r--fts_lib/ftsTime.c84
-rw-r--r--fts_lib/ftsTime.h74
-rw-r--r--fts_lib/ftsTool.c798
-rw-r--r--fts_lib/ftsTool.h58
-rw-r--r--fts_limits.h2045
-rw-r--r--fts_proc.c3744
29 files changed, 29633 insertions, 0 deletions
diff --git a/Kbuild b/Kbuild
new file mode 100644
index 0000000..5bf2b8f
--- /dev/null
+++ b/Kbuild
@@ -0,0 +1,8 @@
+# FTM5 support
+obj-$(CONFIG_TOUCHSCREEN_FTS) += ftm5.o
+ftm5-objs = fts.o fts_proc.o
+ftm5-objs += \
+ fts_lib/ftsCompensation.o fts_lib/ftsCore.o fts_lib/ftsError.o \
+ fts_lib/ftsFrame.o fts_lib/ftsIO.o fts_lib/ftsTest.o fts_lib/ftsTime.o \
+ fts_lib/ftsTool.o fts_lib/ftsFlash.o fts_lib/ftsGesture.o
+
diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..294925c
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,13 @@
+#
+# STMicroelectronics TOUCH driver configuration
+#
+
+config TOUCHSCREEN_FTS
+ tristate "STMicroelectronics multitouch touchscreen - FingerTipS (FTM5)"
+ depends on SPI
+ select TOUCHSCREEN_HEATMAP
+ help
+ Say Y here to enable STMicroelectronics (FTM5) touchscreen support.
+ If unsure, say N.
+ To compile this driver as a module, choose M here: the module
+ will be called ftm5.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c5fe17b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
+M ?= $(shell pwd)
+
+KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_FTS=m
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) $(@)
+
diff --git a/fts.c b/fts.c
new file mode 100644
index 0000000..5a8151f
--- /dev/null
+++ b/fts.c
@@ -0,0 +1,6300 @@
+/**
+ * fts.c
+ *
+ * FTS Capacitive touch screen controller (FingerTipS)
+ *
+ * Copyright (C) 2016, 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.
+ *
+ * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+ * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+ * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM
+ * THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
+ * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ */
+
+
+/*!
+ * \file fts.c
+ * \brief It is the main file which contains all the most important functions
+ * generally used by a device driver the driver
+ */
+#include <linux/device.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/spi/spi.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/of.h>
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+
+#ifdef KERNEL_ABOVE_2_6_38
+#include <linux/input/mt.h>
+#endif
+
+#include <drm/drm_panel.h>
+#include <video/display_timing.h>
+
+
+#include "fts.h"
+#include "fts_lib/ftsCompensation.h"
+#include "fts_lib/ftsCore.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"
+
+/* Touch simulation MT slot */
+#define TOUCHSIM_SLOT_ID 0
+#define TOUCHSIM_TIMER_INTERVAL_NS 8333333
+
+/* Switch GPIO values */
+#define FTS_SWITCH_GPIO_VALUE_AP_MASTER 0
+#define FTS_SWITCH_GPIO_VALUE_SLPI_MASTER 1
+
+/**
+ * Event handler installer helpers
+ */
+#define event_id(_e) (EVT_ID_##_e >> 4)
+#define handler_name(_h) fts_##_h##_event_handler
+
+#define install_handler(_i, _evt, _hnd) \
+ do { \
+ _i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd); \
+ } while (0)
+
+
+/* Use decimal-formatted raw data */
+#define RAW_DATA_FORMAT_DEC
+
+#ifdef KERNEL_ABOVE_2_6_38
+#define TYPE_B_PROTOCOL
+#endif
+
+
+extern SysInfo systemInfo;
+extern TestToDo tests;
+#ifdef GESTURE_MODE
+extern struct mutex gestureMask_mutex;
+#endif
+
+char fts_ts_phys[64]; /* /< buffer which store the input device name
+ * assigned by the kernel */
+
+static u32 typeOfCommand[CMD_STR_LEN] = { 0 }; /* /< buffer used to store the
+ * command sent from the MP
+ * device file node */
+static int numberParameters; /* /< number of parameter passed through the MP
+ * device file node */
+#ifdef USE_ONE_FILE_NODE
+static int feature_feasibility = ERROR_OP_NOT_ALLOW;
+#endif
+#ifdef GESTURE_MODE
+static u8 mask[GESTURE_MASK_SIZE + 2];
+extern u16 gesture_coordinates_x[GESTURE_MAX_COORDS_PAIRS_REPORT];
+extern u16 gesture_coordinates_y[GESTURE_MAX_COORDS_PAIRS_REPORT];
+extern int gesture_coords_reported;
+extern struct mutex gestureMask_mutex;
+#endif
+
+#ifdef PHONE_KEY
+static u8 key_mask; /* /< store the last update of the key mask
+ * published by the IC */
+#endif
+
+static int fts_init_sensing(struct fts_ts_info *info);
+static int fts_mode_handler(struct fts_ts_info *info, int force);
+static void fts_pinctrl_setup(struct fts_ts_info *info, bool active);
+
+static int fts_chip_initialization(struct fts_ts_info *info, int init_type);
+#ifdef SUPPORT_PROX_PALM
+static ssize_t audio_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t audio_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t prox_palm_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t prox_palm_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+#endif
+
+/**
+ * Release all the touches in the linux input subsystem
+ * @param info pointer to fts_ts_info which contains info about device/hw setup
+ */
+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;
+ else
+ type = MT_TOOL_FINGER;
+#endif
+ input_mt_slot(info->input_dev, i);
+ input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
+ input_mt_report_slot_state(info->input_dev, type, 0);
+ input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1);
+ }
+ input_report_key(info->input_dev, BTN_TOUCH, 0);
+ input_sync(info->input_dev);
+ info->touch_id = 0;
+ info->palm_touch_mask = 0;
+ info->grip_touch_mask = 0;
+#ifdef STYLUS_MODE
+ info->stylus_id = 0;
+#endif
+}
+
+
+/**
+ * @defgroup file_nodes Driver File Nodes
+ * Driver publish a series of file nodes used to provide several utilities
+ * to the host and give him access to different API.
+ * @{
+ */
+
+/**
+ * @defgroup device_file_nodes Device File Nodes
+ * @ingroup file_nodes
+ * Device File Nodes \n
+ * There are several file nodes that are associated to the device and which
+ * are designed to be used by the host to enable/disable features or trigger
+ * some system specific actions \n
+ * Usually their final path depend on the definition of device tree node of
+ * the IC (e.g /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049)
+ * @{
+ */
+/***************************************** FW UPGGRADE
+ * ***************************************************/
+
+/**
+ * File node function to Update firmware from shell \n
+ * echo path_to_fw X Y > fwupdate perform a fw update \n
+ * where: \n
+ * path_to_fw = file name or path of the the FW to burn, if "NULL" the default
+ * approach selected in the driver will be used\n
+ * X = 0/1 to force the FW update whichever fw_version and config_id;
+ * 0=perform a fw update only if the fw in the file is newer than the fw in the
+ * chip \n
+ * Y = 0/1 keep the initialization data; 0 = will erase the initialization data
+ * from flash, 1 = will keep the initialization data
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 no
+ * error) \n
+ * } = end byte
+ */
+static ssize_t fts_fwupdate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, mode[2];
+ char path[100 + 1]; /* extra byte to hold '\0'*/
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ /* default(if not specified by user) set force = 0 and keep_cx to 1 */
+ mode[0] = 0;
+ mode[1] = 1;
+
+ /* reading out firmware upgrade parameters */
+ if (sscanf(buf, "%100s %d %d", path, &mode[0], &mode[1]) >= 1) {
+ pr_info("%s: file = %s, force = %d, keep_cx = %d\n", __func__,
+ path, mode[0], mode[1]);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true);
+
+ if (info->sensor_sleep)
+ ret = ERROR_BUS_WR;
+ else
+ ret = flashProcedure(path, mode[0], mode[1]);
+
+ info->fwupdate_stat = ret;
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+
+ if (ret == ERROR_BUS_WR)
+ pr_err("%s: bus is not accessible. ERROR %08X\n",
+ __func__, ret);
+ else if (ret < OK)
+ pr_err("%s Unable to upgrade firmware! ERROR %08X\n",
+ __func__, ret);
+ } else
+ pr_err("%s: Wrong number of parameters! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return count;
+}
+
+static ssize_t fts_fwupdate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ /* fwupdate_stat: ERROR code Returned by flashProcedure. */
+ return scnprintf(buf, PAGE_SIZE, "{ %08X }\n", info->fwupdate_stat);
+}
+
+
+/***************************************** UTILITIES
+ * (current fw_ver/conf_id, active mode, file fw_ver/conf_id)
+ ***************************************************/
+/**
+ * File node to show on terminal external release version in Little Endian \n
+ * (first the less significant byte) \n
+ * cat appid show the external release version of the FW running in the IC
+ */
+static ssize_t fts_appid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int written = 0;
+ char temp[35];
+
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "REL: %s\n",
+ printHex("",
+ systemInfo.u8_releaseInfo,
+ EXTERNAL_RELEASE_INFO_SIZE,
+ temp,
+ sizeof(temp)));
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "FW: %04X\nCFG: %04X\nAFE: %02X\nProject: %04X\n",
+ systemInfo.u16_fwVer, systemInfo.u16_cfgVer,
+ systemInfo.u8_cfgAfeVer,
+ systemInfo.u16_cfgProjectId);
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "FW file: %s\n", info->board->fw_name);
+
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "Extended display info: ");
+ if (!info->extinfo.is_read)
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "[pending]");
+ else if (info->extinfo.size == 0)
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "[none]");
+ else if (info->extinfo.size * 2 < PAGE_SIZE - written) {
+ bin2hex(buf + written, info->extinfo.data, info->extinfo.size);
+ written += info->extinfo.size * 2;
+ }
+
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "\nMPFlag: %02X\n",
+ systemInfo.u8_mpFlag);
+
+ return written;
+}
+
+/**
+ * File node to show on terminal the mode that is active on the IC \n
+ * cat mode_active to show the bitmask which indicate
+ * the modes/features which are running on the IC in a specific instant of time
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1 = 1 byte in HEX format which represent the actual running scan mode
+ * (@link scan_opt Scan Mode Options @endlink) \n
+ * X2 = 1 byte in HEX format which represent the bitmask on which is running
+ * the actual scan mode \n
+ * X3X4 = 2 bytes in HEX format which represent a bitmask of the features that
+ * are enabled at this moment (@link feat_opt Feature Selection Options
+ * @endlink) \n
+ * } = end byte
+ * @see fts_mode_handler()
+ */
+static ssize_t fts_mode_active_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("Current mode active = %08X\n", info->mode);
+ return scnprintf(buf, PAGE_SIZE, "{ %08X }\n", info->mode);
+}
+
+/**
+ * File node to show the fw_ver and config_id of the FW file
+ * cat fw_file_test show on the kernel log external release
+ * 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 fts_ts_info *info = dev_get_drvdata(dev);
+ Firmware fw;
+ int ret;
+ char temp[100] = { 0 };
+
+ fw.data = NULL;
+ ret = readFwFile(info->board->fw_name, &fw, 0);
+
+ if (ret < OK)
+ pr_err("Error during reading FW file! ERROR %08X\n", ret);
+ else
+ pr_info("%s, size = %d bytes\n",
+ printHex("EXT Release = ",
+ systemInfo.u8_releaseInfo,
+ EXTERNAL_RELEASE_INFO_SIZE,
+ temp,
+ sizeof(temp)),
+ fw.data_size);
+ kfree(fw.data);
+ return 0;
+}
+
+static ssize_t fts_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ u8 *dump = NULL;
+ int dumpSize = ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE;
+ u8 reg;
+ int written = 0;
+ int res;
+ int i;
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ pr_err("%s: bus is not accessible.\n", __func__);
+ written += scnprintf(buf, PAGE_SIZE,
+ "Bus is not accessible.\n");
+ goto exit;
+ }
+
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "Mode: 0x%08X\n", info->mode);
+
+ res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, ADDR_ICR,
+ &reg, 1, DUMMY_HW_REG);
+ if (res < 0)
+ pr_err("%s: failed to read ICR.\n", __func__);
+ else
+ written += scnprintf(buf + written, PAGE_SIZE - written,
+ "ICR: 0x%02X\n", reg);
+
+ dump = kzalloc(dumpSize, GFP_KERNEL);
+ if (!dump) {
+ written += strlcat(buf + written, "Buffer allocation failed!\n",
+ PAGE_SIZE - written);
+ goto exit;
+ }
+
+ res = dumpErrorInfo(dump, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE);
+ if (res >= 0) {
+ written += strlcat(buf + written, "Error dump:",
+ PAGE_SIZE - written);
+ for (i = 0; i < dumpSize; i++) {
+ if (i % 8 == 0)
+ written += scnprintf(buf + written,
+ PAGE_SIZE - written,
+ "\n%02X: ", i);
+ written += scnprintf(buf + written,
+ PAGE_SIZE - written,
+ "%02X ", dump[i]);
+ }
+ written += strlcat(buf + written, "\n", PAGE_SIZE - written);
+ }
+
+exit:
+ kfree(dump);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return written;
+}
+
+#if 0
+/**
+ * File node to obtain and show strength frame
+ * cat strength_frame to obtain strength data \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 no
+ *error) \n
+ * **** if error code is all 0s **** \n
+ * FF = 1 byte in HEX format number of rows \n
+ * SS = 1 byte in HEX format number of columns \n
+ * N1, ... = the decimal value of each node separated by a coma \n
+ * ********************************* \n
+ * } = end byte
+ */
+static ssize_t fts_strength_frame_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ MutualSenseFrame frame;
+ int res, count, j, size = (6 * 2) + 1, index = 0;
+ char *all_strbuff = NULL;
+ /* char buff[CMD_STR_LEN] = {0}; */
+ /* struct i2c_client *client = to_i2c_client(dev); */
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ frame.node_data = NULL;
+
+ res = fts_enableInterrupt(false);
+ if (res < OK)
+ goto END;
+
+ res = senseOn();
+ if (res < OK) {
+ pr_err("%s: could not start scanning! ERROR %08X\n",
+ __func__, res);
+ goto END;
+ }
+ mdelay(WAIT_FOR_FRESH_FRAMES);
+ res = senseOff();
+ if (res < OK) {
+ pr_err("%s: could not finish scanning! ERROR %08X\n",
+ __func__, res);
+ goto END;
+ }
+
+ mdelay(WAIT_AFTER_SENSEOFF);
+ flushFIFO();
+
+ res = getMSFrame3(MS_STRENGTH, &frame);
+ if (res < OK) {
+ pr_err("%s: could not get the frame! ERROR %08X\n",
+ __func__, res);
+ goto END;
+ } else {
+ size += (res * 6);
+ pr_info("The frame size is %d words\n", 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 = (char *)kzalloc(size * sizeof(char), GFP_KERNEL);
+
+ if (all_strbuff != NULL) {
+ snprintf(&all_strbuff[index], 11, "{ %08X", res);
+
+ index += 10;
+
+ if (res >= OK) {
+ snprintf(&all_strbuff[index], 3, "%02X",
+ (u8)frame.header.force_node);
+ index += 2;
+ snprintf(&all_strbuff[index], 3, "%02X",
+ (u8)frame.header.sense_node);
+
+ index += 2;
+
+ for (j = 0; j < frame.node_data_size; j++) {
+ snprintf(&all_strbuff[index], 10, "%d,%n",
+ frame.node_data[j], &count);
+ index += count;
+ }
+
+ kfree(frame.node_data);
+ }
+
+ snprintf(&all_strbuff[index], 3, " }");
+ index += 2;
+
+ count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff);
+ kfree(all_strbuff);
+ } else
+ pr_err("%s: Unable to allocate all_strbuff! ERROR %08X\n",
+ __func__, ERROR_ALLOC);
+
+ fts_enableInterrupt(true);
+ return count;
+}
+#endif
+
+/***************************************** 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 */
+/**
+ * Check if there is any conflict in enable/disable a particular feature
+ * considering the features already enabled and running
+ * @param info pointer to fts_ts_info which contains info about the device
+ * and its hw setup
+ * @param feature code of the feature that want to be tested
+ * @return OK if is possible to enable/disable feature, ERROR_OP_NOT_ALLOW
+ * in case of any other conflict
+ */
+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 */
+ /*res=ERROR_OP_NOT_ALLOW;
+ * if(info->resume_bit ==0){
+ * switch(feature){
+ #ifdef GESTURE_MODE
+ * case FEAT_SEL_GESTURE:
+ * res = OK;
+ * break;
+ #endif
+ * default:
+ * pr_err("%s: Feature not allowed in this
+ * operating mode! ERROR %08X\n", __func__, res);
+ * break;
+ *
+ * }
+ * }else{
+ * switch(feature){
+ #ifdef GESTURE_MODE
+ * case FEAT_SEL_GESTURE:
+ #endif
+ * case FEAT__SEL_GLOVE: // glove mode can only activate
+ *during sense on
+ * res = OK;
+ * break;
+ *
+ * default:
+ * pr_err("%s: Feature not allowed in this
+ * operating mode! ERROR %08X\n", __func__, res);
+ * break;
+ *
+ * }
+ * }*/
+
+
+/* Example based only on the feature that is going to be activated */
+ switch (feature) {
+ case FEAT_SEL_GESTURE:
+ if (info->cover_enabled == 1) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("%s: Feature not allowed when in Cover mode! ERROR %08X\n",
+ __func__, res);
+ /* for example here can be placed a code for disabling
+ * the cover mode when gesture is activated */
+ }
+ break;
+
+ case FEAT_SEL_GLOVE:
+ if (info->gesture_enabled == 1) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("%s: Feature not allowed when Gestures enabled! ERROR %08X\n",
+ __func__, res);
+ /* for example here can be placed 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:
+ pr_info("%s: Feature Allowed!\n", __func__);
+ }
+
+ return res;
+}
+
+#ifdef USE_ONE_FILE_NODE
+/**
+ * File node to enable some feature
+ * echo XX 00/01 > feature_enable to enable/disable XX
+ * (possible values @link feat_opt Feature Selection Options @endlink) feature
+ * cat feature_enable to show the result of enabling/disabling process
+ * echo 01/00 > feature_enable; cat feature_enable to perform
+ * both actions stated before in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 =
+ * no error) \n
+ * } = end byte
+ */
+static ssize_t fts_feature_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ char *p = (char *)buf;
+ unsigned int temp, temp2;
+ int res = OK;
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+ if ((count - 2 + 1) / 3 != 1)
+ pr_err("fts_feature_enable: Number of parameter wrong! %d > %d\n",
+ (count - 2 + 1) / 3, 1);
+ else {
+ if (sscanf(p, "%02X %02X ", &temp, &temp2) == 2) {
+ p += 3;
+ res = check_feature_feasibility(info, temp);
+ if (res >= OK) {
+ switch (temp) {
+ #ifdef GESTURE_MODE
+ case FEAT_SEL_GESTURE:
+ info->gesture_enabled = temp2;
+ pr_info("fts_feature_enable: Gesture Enabled = %d\n",
+ info->gesture_enabled);
+ break;
+ #endif
+
+ #ifdef GLOVE_MODE
+ case FEAT_SEL_GLOVE:
+ info->glove_enabled = temp2;
+ pr_info("fts_feature_enable: Glove Enabled = %d\n",
+ info->glove_enabled);
+ break;
+ #endif
+
+ #ifdef STYLUS_MODE
+ case FEAT_SEL_STYLUS:
+ info->stylus_enabled = temp2;
+ pr_info("fts_feature_enable: Stylus Enabled = %d\n",
+ info->stylus_enabled);
+ break;
+ #endif
+
+ #ifdef COVER_MODE
+ case FEAT_SEL_COVER:
+ info->cover_enabled = temp2;
+ pr_info("fts_feature_enable: Cover Enabled = %d\n",
+ info->cover_enabled);
+ break;
+ #endif
+
+ #ifdef CHARGER_MODE
+ case FEAT_SEL_CHARGER:
+ info->charger_enabled = temp2;
+ pr_info("fts_feature_enable: Charger Enabled = %d\n",
+ info->charger_enabled);
+ break;
+ #endif
+
+ #ifdef GRIP_MODE
+ case FEAT_SEL_GRIP:
+ info->grip_enabled = temp2;
+ pr_info("fts_feature_enable: Grip Enabled = %d\n",
+ info->grip_enabled);
+ break;
+ #endif
+
+
+
+ default:
+ pr_err("fts_feature_enable: Feature %08X not valid! ERROR %08X\n",
+ 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
+ pr_err("%s: Call echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n",
+ __func__, res);
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+}
+
+
+
+static ssize_t fts_feature_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+
+ if (feature_feasibility < OK)
+ pr_err("%s: Call before echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n",
+ __func__, feature_feasibility);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ feature_feasibility);
+
+ feature_feasibility = ERROR_OP_NOT_ALLOW;
+ return count;
+}
+
+#else
+
+
+#ifdef GRIP_MODE
+/**
+ * File node to set the grip mode
+ * echo 01/00 > grip_mode to enable/disable glove mode \n
+ * cat grip_mode to show the status of the grip_enabled switch \n
+ * echo 01/00 > grip_mode; cat grip_mode to enable/disable grip
+ *mode
+ * and see the switch status in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent the value
+ * info->grip_enabled (1 = enabled; 0= disabled) \n
+ * } = end byte
+ */
+static ssize_t fts_grip_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("%s: grip_enabled = %d\n", __func__,
+ info->grip_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->grip_enabled);
+
+ return count;
+}
+
+
+static ssize_t fts_grip_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 fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+ /* 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)
+ pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
+ __func__, (count + 1) / 3);
+ else {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+
+/* 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_SEL_GRIP);
+ if (res >= OK || temp == FEAT_DISABLE) {
+ info->grip_enabled = temp;
+ res = fts_mode_handler(info, 1);
+ if (res < OK)
+ pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
+ __func__, res);
+ }
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+}
+#endif
+
+#ifdef CHARGER_MODE
+/**
+ * File node to set the glove mode
+ * echo XX/00 > charger_mode to value >0 to enable
+ * (possible values: @link charger_opt Charger Options @endlink),
+ * 00 to disable charger mode \n
+ * cat charger_mode to show the status of the charger_enabled switch \n
+ * echo 01/00 > charger_mode; cat charger_mode to enable/disable
+ * charger mode and see the switch status in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent the value
+ * info->charger_enabled (>0 = enabled; 0= disabled) \n
+ * } = end byte
+ */
+static ssize_t fts_charger_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("%s: charger_enabled = %d\n", __func__,
+ info->charger_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->charger_enabled);
+ 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 fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+/* 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)
+ pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
+ __func__, (count + 1) / 3);
+ else {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+
+/** 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_SEL_CHARGER);
+ if (res >= OK || temp == FEAT_DISABLE) {
+ info->charger_enabled = temp;
+ res = fts_mode_handler(info, 1);
+ if (res < OK)
+ pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
+ __func__, res);
+ }
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+}
+#endif
+
+#ifdef GLOVE_MODE
+/**
+ * File node to set the glove mode
+ * echo 01/00 > glove_mode to enable/disable glove mode \n
+ * cat glove_mode to show the status of the glove_enabled switch \n
+ * echo 01/00 > glove_mode; cat glove_mode to enable/disable glove mode and
+ * see the switch status in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent the of value
+ * info->glove_enabled (1 = enabled; 0= disabled) \n
+ * } = end byte
+ */
+static ssize_t fts_glove_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("%s: glove_enabled = %d\n", __func__, info->glove_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->glove_enabled);
+
+ 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 fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+/* 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)
+ pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
+ __func__, (count + 1) / 3);
+ else {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+
+/* 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_SEL_GLOVE);
+ if (res >= OK || temp == FEAT_DISABLE) {
+ info->glove_enabled = temp;
+ res = fts_mode_handler(info, 1);
+ if (res < OK)
+ pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
+ __func__, res);
+ }
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ 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 */
+/**
+ * File node to set the cover mode
+ * echo 01/00 > cover_mode to enable/disable cover mode \n
+ * cat cover_mode to show the status of the cover_enabled switch \n
+ * echo 01/00 > cover_mode; cat cover_mode to enable/disable cover mode
+ * and see the switch status in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which is the value of info->cover_enabled
+ * (1 = enabled; 0= disabled)\n
+ * } = end byte \n
+ * NOTE: \n
+ * 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)
+{
+ int count = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("%s: cover_enabled = %d\n", __func__, info->cover_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->cover_enabled);
+
+ 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 fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+/* 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)
+ pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
+ __func__, (count + 1) / 3);
+ else {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+
+/* 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_SEL_COVER);
+ if (res >= OK || temp == FEAT_DISABLE) {
+ info->cover_enabled = temp;
+ res = fts_mode_handler(info, 1);
+ if (res < OK)
+ pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
+ __func__, res);
+ }
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+}
+#endif
+
+#ifdef STYLUS_MODE
+/**
+ * File node to enable the stylus report
+ * echo 01/00 > stylus_mode to enable/disable stylus mode \n
+ * cat stylus_mode to show the status of the stylus_enabled switch \n
+ * echo 01/00 > stylus_mode; cat stylus_mode to enable/disable stylus mode
+ * and see the switch status in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which is the value of info->stylus_enabled
+ * (1 = enabled; 0= disabled)\n
+ * } = end byte
+ */
+static ssize_t fts_stylus_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("%s: stylus_enabled = %d\n", __func__, info->stylus_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->stylus_enabled);
+
+ 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;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+
+/* 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)
+ pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
+ __func__, (count + 1) / 3);
+ else {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+ info->stylus_enabled = temp;
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+
+ return count;
+}
+#endif
+
+#endif
+
+/***************************************** GESTURES
+ ***************************************************/
+#ifdef GESTURE_MODE
+#ifdef USE_GESTURE_MASK /* if this define is used, a gesture bit mask
+ * is used as method to select the gestures to
+ * enable/disable */
+
+/**
+ * File node used by the host to set the gesture mask to enable or disable
+ * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
+ * EE = 00(disable) or 01(enable) \n
+ * X1 ~~ = gesture mask (example 06 00 ~~ 00 this gesture mask represents
+ * the gestures with ID = 1 and 2) can be specified
+ * from 1 to GESTURE_MASK_SIZE bytes, \n
+ * if less than GESTURE_MASK_SIZE bytes are passed as arguments,
+ * the omit bytes of the mask maintain the previous settings \n
+ * 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 \n
+ * cat gesture_mask set inside the specified mask and return an error code
+ * for the operation \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error code for enabling
+ * the mask (00000000 = no error)\n
+ * } = end byte \n\n
+ * if USE_GESTURE_MASK is not define the usage of the function become: \n\n
+ * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
+ * EE = 00(disable) or 01(enable) \n
+ * X1 ~~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2
+ * and 5)
+ * there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture
+ * IDs @endlink) \n
+ * 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. \n
+ * cat gesture_mask to show the status of the gesture enabled switch \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled
+ * (1 = enabled; 0= disabled)\n
+ * } = end byte
+ */
+static ssize_t fts_gesture_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0, res, temp;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ scnprintf(buf, PAGE_SIZE, "{ %08X }\n", res);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+ if (mask[0] == 0) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("%s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n",
+ __func__, 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)
+ pr_err("fts_gesture_mask_store: ERROR %08X\n", res);
+ }
+ res |= check_feature_feasibility(info, FEAT_SEL_GESTURE);
+ temp = isAnyGestureActive();
+ if (res >= OK || temp == FEAT_DISABLE)
+ info->gesture_enabled = temp;
+
+ pr_info("fts_gesture_mask_store: Gesture Enabled = %d\n",
+ info->gesture_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n", res);
+ mask[0] = 0;
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ 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) {
+ pr_err("fts_gesture_mask_store: Number of bytes of parameter wrong! %zu > (enable/disable + %d )\n",
+ (count + 1) / 3, GESTURE_MASK_SIZE);
+ mask[0] = 0;
+ } else {
+ mask[0] = ((count + 1) / 3) - 1;
+ for (n = 1; n <= (count + 1) / 3; n++) {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+ mask[n] = (u8)temp;
+ pr_info("mask[%d] = %02X\n", n, mask[n]);
+ } else
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ }
+ }
+
+ 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,
+ * 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 */
+/**
+ * File node used by the host to set the gesture mask to enable or disable
+ * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
+ * EE = 00(disable) or 01(enable) \n
+ * X1 ~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2
+ * and 5)
+ * there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture
+ * IDs @endlink) \n
+ * 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 \n
+ * cat gesture_mask to show the status of the gesture enabled switch \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled
+ * (1 = enabled; 0= disabled)\n
+ * } = end byte
+ */
+static ssize_t fts_gesture_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ pr_info("fts_gesture_mask_show: gesture_enabled = %d\n",
+ info->gesture_enabled);
+
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "{ %08X }\n",
+ info->gesture_enabled);
+
+
+ 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 fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+ }
+
+ if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) {
+ pr_err("fts_gesture_mask_store: Number of bytes of parameter wrong! %d < or > (enable/disable + at least one gestureID or max %d bytes)\n",
+ (count + 1) / 3, GESTURE_MASK_SIZE);
+ mask[0] = 0;
+ } else {
+ memset(mask, 0, GESTURE_MASK_SIZE + 2);
+ mask[0] = ((count + 1) / 3) - 1;
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+ mask[1] = (u8)temp;
+ for (n = 1; n < (count + 1) / 3; n++) {
+ if (sscanf(p, "%02X ", &temp) == 1) {
+ p += 3;
+ fromIDtoMask((u8)temp, &mask[2],
+ GESTURE_MASK_SIZE);
+ } else {
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ mask[0] = 0;
+ goto END;
+ }
+ }
+
+ for (n = 0; n < GESTURE_MASK_SIZE + 2; n++)
+ pr_info("mask[%d] = %02X\n", n, mask[n]);
+ } else {
+ pr_err("%s: Error when reading with sscanf!\n",
+ __func__);
+ mask[0] = 0;
+ }
+ }
+
+END:
+ if (mask[0] == 0) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("%s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n",
+ __func__, 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)
+ pr_err("fts_gesture_mask_store: ERROR %08X\n", res);
+ }
+
+ res = check_feature_feasibility(info, FEAT_SEL_GESTURE);
+ temp = isAnyGestureActive();
+ if (res >= OK || temp == FEAT_DISABLE)
+ info->gesture_enabled = temp;
+ res = fts_mode_handler(info, 0);
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ return count;
+}
+
+
+#endif
+
+
+/**
+ * File node to read the coordinates of the last gesture drawn by the user \n
+ * cat gesture_coordinates to obtain the gesture coordinates \n
+ * the string returned in the shell follow this up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 =
+ *OK) \n
+ * \n if error code = 00000000 \n
+ * CC = 1 byte in HEX format number of coords (pair of x,y) returned \n
+ * XXiYYi ... = XXi 2 bytes in HEX format for x[i] and
+ * YYi 2 bytes in HEX format for y[i] (big endian) \n
+ * \n
+ * } = end byte
+ */
+static ssize_t fts_gesture_coordinates_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int size = PAGE_SIZE;
+ int count = 0, res, i = 0;
+
+ pr_info("%s: Getting gestures coordinates...\n", __func__);
+
+ if (gesture_coords_reported < OK) {
+ pr_err("%s: invalid coordinates! ERROR %08X\n",
+ __func__, gesture_coords_reported);
+ res = gesture_coords_reported;
+ } else {
+ size += gesture_coords_reported * 2 * 4 + 2;
+ /* coords are pairs of x,y (*2) where each coord is
+ * short(2bytes=4char)(*4) + 1 byte(2char) num of coords (+2)
+ **/
+ res = OK; /* set error code to OK */
+ }
+
+
+ count += scnprintf(buf + count,
+ size - count, "{ %08X", res);
+
+ if (res >= OK) {
+ count += scnprintf(buf + count,
+ size - count, "%02X",
+ gesture_coords_reported);
+
+ for (i = 0; i < gesture_coords_reported; i++) {
+ count += scnprintf(buf + count,
+ size - count,
+ "%04X",
+ gesture_coordinates_x[i]);
+ count += scnprintf(buf + count,
+ size - count,
+ "%04X",
+ gesture_coordinates_y[i]);
+ }
+ }
+
+ count += scnprintf(buf + count, size - count, " }\n");
+ pr_info("%s: Getting gestures coordinates FINISHED!\n", __func__);
+
+ return count;
+}
+#endif
+
+/* Touch simulation hr timer expiry callback */
+static enum hrtimer_restart touchsim_timer_cb(struct hrtimer *timer)
+{
+ struct fts_touchsim *touchsim = container_of(timer,
+ struct fts_touchsim,
+ hr_timer);
+ enum hrtimer_restart retval = HRTIMER_NORESTART;
+
+ if (touchsim->is_running) {
+ hrtimer_forward_now(timer,
+ ns_to_ktime(TOUCHSIM_TIMER_INTERVAL_NS));
+ retval = HRTIMER_RESTART;
+ }
+
+ /* schedule the task to report touch coordinates to kernel
+ * input subsystem
+ */
+ queue_work(touchsim->wq, &touchsim->work);
+
+ return retval;
+}
+
+/* Compute the next touch coordinate(x,y) */
+static void touchsim_refresh_coordinates(struct fts_touchsim *touchsim)
+{
+ struct fts_ts_info *info = container_of(touchsim,
+ struct fts_ts_info,
+ touchsim);
+
+ const int x_start = info->board->x_axis_max / 10;
+ const int x_max = (info->board->x_axis_max * 9) / 10;
+ const int y_start = info->board->y_axis_max / 4;
+ const int y_max = info->board->y_axis_max / 2;
+
+ touchsim->x += touchsim->x_step;
+ touchsim->y += touchsim->y_step;
+
+ if (touchsim->x < x_start || touchsim->x > x_max)
+ touchsim->x_step *= -1;
+
+ if (touchsim->y < y_start || touchsim->y > y_max)
+ touchsim->y_step *= -1;
+}
+
+/* Report touch contact */
+static void touchsim_report_contact_event(struct input_dev *dev, int slot_id,
+ int x, int y, int z)
+{
+ /* report the cordinates to the input subsystem */
+ input_mt_slot(dev, slot_id);
+ input_report_key(dev, BTN_TOUCH, true);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(dev, ABS_MT_PRESSURE, z);
+}
+
+/* Work callback to report the touch co-ordinates to input subsystem */
+static void touchsim_work(struct work_struct *work)
+{
+ struct fts_touchsim *touchsim = container_of(work,
+ struct fts_touchsim,
+ work);
+ struct fts_ts_info *info = container_of(touchsim,
+ struct fts_ts_info,
+ touchsim);
+#ifdef TOUCHSCREEN_HEATMAP
+ ktime_t timestamp = ktime_get();
+#endif
+
+ /* prevent CPU from entering deep sleep */
+ pm_qos_update_request(&info->pm_qos_req, 100);
+
+ /* Notify the PM core that the wakeup event will take 1 sec */
+ __pm_wakeup_event(info->wakesrc, jiffies_to_msecs(HZ));
+
+ /* get the next touch coordinates */
+ touchsim_refresh_coordinates(touchsim);
+
+ /* send the touch co-ordinates */
+ touchsim_report_contact_event(info->input_dev, TOUCHSIM_SLOT_ID,
+ touchsim->x, touchsim->y, 1);
+
+ input_sync(info->input_dev);
+
+#ifdef TOUCHSCREEN_HEATMAP
+ heatmap_read(&info->v4l2, ktime_to_ns(timestamp));
+#endif
+
+ pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+}
+
+/* Start the touch simulation */
+static int touchsim_start(struct fts_touchsim *touchsim)
+{
+ struct fts_ts_info *info = container_of(touchsim,
+ struct fts_ts_info,
+ touchsim);
+ int res;
+
+ if (!touchsim->wq) {
+ pr_err("%s: touch simulation test wq is not available!\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ if (touchsim->is_running) {
+ pr_err("%s: test in progress!\n", __func__);
+ return -EBUSY;
+ }
+
+ /* setup the initial touch coordinates*/
+ touchsim->x = info->board->x_axis_max / 10;
+ touchsim->y = info->board->y_axis_max / 4;
+
+ touchsim->is_running = true;
+
+ touchsim->x_step = 2;
+ touchsim->y_step = 2;
+
+ /* Disable touch interrupts from hw */
+ res = fts_enableInterrupt(false);
+ if ( res != OK)
+ pr_err("%s: fts_enableInterrupt: ERROR %08X\n", __func__, res);
+
+ /* Release all touches in the linux input subsystem */
+ release_all_touches(info);
+
+ /* setup and start a hr timer to be fired every 120Hz(~8.333333ms) */
+ hrtimer_init(&touchsim->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ touchsim->hr_timer.function = touchsim_timer_cb;
+ hrtimer_start(&touchsim->hr_timer,
+ ns_to_ktime(TOUCHSIM_TIMER_INTERVAL_NS),
+ HRTIMER_MODE_ABS);
+
+ return OK;
+}
+
+/* Stop the touch simulation test */
+static int touchsim_stop(struct fts_touchsim *touchsim)
+{
+ struct fts_ts_info *info = container_of(touchsim,
+ struct fts_ts_info,
+ touchsim);
+ int res;
+
+ if (!touchsim->is_running) {
+ pr_err("%s: test is not in progress!\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Set the flag here to make sure flushed work doesn't
+ * re-start the timer
+ */
+ touchsim->is_running = false;
+
+ hrtimer_cancel(&touchsim->hr_timer);
+
+ /* flush any pending work */
+ flush_workqueue(touchsim->wq);
+
+ /* Release all touches in the linux input subsystem */
+ release_all_touches(info);
+
+ /* re enable the hw touch interrupt */
+ res = fts_enableInterrupt(true);
+ if ( res != OK)
+ pr_err("%s: fts_enableInterrupt: ERROR %08X\n", __func__, res);
+
+
+ return OK;
+}
+
+/** sysfs file node to handle the touch simulation test request.
+ * "cat touchsim" shows if the test is running
+ * Possible outputs:
+ * 1 = test running.
+ * 0 = test not running.
+ */
+static ssize_t fts_touch_simulation_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ info->touchsim.is_running ? 1 : 0);
+}
+
+/** sysfs file node to handle the touch simulation test request.
+ * "echo <cmd> > touchsim" to execute a command
+ * Possible commands (cmd):
+ * 1 = start the test if not already running.
+ * 0 = stop the test if its running.
+ */
+static ssize_t fts_touch_simulation_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ ssize_t retval = count;
+ u8 result;
+
+ if (!mutex_trylock(&info->diag_cmd_lock)) {
+ pr_err("%s: Blocking concurrent access\n", __func__);
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (kstrtou8(buf, 16, &result)) {
+ pr_err("%s:bad input. valid inputs are either 0 or 1!\n",
+ __func__);
+ retval = -EINVAL;
+ goto unlock;
+ }
+
+ if (result == 1)
+ touchsim_start(&info->touchsim);
+ else if (result == 0)
+ touchsim_stop(&info->touchsim);
+ else
+ pr_err("%s:Invalid cmd(%u). valid cmds are either 0 or 1!\n",
+ __func__, result);
+unlock:
+ mutex_unlock(&info->diag_cmd_lock);
+out:
+ return retval;
+}
+
+/** sysfs file node to show motion filter mode
+ * "echo 0/1 > default_mf" to change
+ * "cat default_mf" to show
+ * Possible commands:
+ * 0 = Dynamic change motion filter
+ * 1 = Default motion filter by FW
+ */
+static ssize_t fts_default_mf_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", info->use_default_mf ? 1 : 0);
+}
+
+static ssize_t fts_default_mf_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ bool val = false;
+ ssize_t retval = count;
+
+ if (!mutex_trylock(&info->diag_cmd_lock)) {
+ pr_err("%s: Blocking concurrent access\n", __func__);
+ retval = -EBUSY;
+ goto out;
+ }
+
+ if (kstrtobool(buf, &val) < 0) {
+ pr_err("%s: bad input. valid inputs are either 0 or 1!\n",
+ __func__);
+ retval = -EINVAL;
+ goto unlock;
+ }
+
+ info->use_default_mf = val;
+
+unlock:
+ mutex_unlock(&info->diag_cmd_lock);
+out:
+ return retval;
+}
+
+/***************************************** PRODUCTION TEST
+ ***************************************************/
+
+/**
+ * File node to execute the Mass Production Test or to get data from the IC
+ * (raw or ms/ss init data)
+ * echo cmd > stm_fts_cmd to execute a command \n
+ * cat stm_fts_cmd to show the result of the command \n
+ * echo cmd > stm_fts_cmd; cat stm_fts_cmd to execute and show the result
+ * in just one call \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * X1X2X3X4 = 4 bytes in HEX format which represent an error_code (00000000 =
+ * OK)\n
+ * (optional) data = data coming from the command executed represented as HEX
+ * string \n
+ * Not all the command return additional data \n
+ * } = end byte \n
+ * \n
+ * Possible commands (cmd): \n
+ * - 00 = MP Test -> return error_code \n
+ * - 01 = ITO Test -> return error_code \n
+ * - 03 = MS Raw Test -> return error_code \n
+ * - 04 = MS Init Data Test -> return error_code \n
+ * - 05 = SS Raw Test -> return error_code \n
+ * - 06 = SS Init Data Test -> return error_code \n
+ * - 13 = Read 1 MS Raw Frame -> return additional data: MS frame row after row
+ * \n
+ * - 14 = Read MS Init Data -> return additional data: MS init data row after
+ * row \n
+ * - 15 = Read 1 SS Raw Frame -> return additional data: SS frame,
+ * force channels followed by sense channels \n
+ * - 16 = Read SS Init Data -> return additional data: SS Init data,
+ * first IX for force and sense channels and then CX for force and sense
+ * channels \n
+ * - F0 = Perform a system reset -> return error_code \n
+ * - F1 = Perform a system reset and reenable the sensing and the interrupt
+ */
+static ssize_t stm_fts_cmd_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u8 result, n = 0;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ char *p, *temp_buf, *token;
+ size_t token_len = 0;
+ ssize_t retval = count;
+
+ if (!count) {
+ pr_err("%s: Invalid input buffer length!\n", __func__);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ if (!info) {
+ pr_err("%s: Unable to access driver data\n", __func__);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ if (!mutex_trylock(&info->diag_cmd_lock)) {
+ pr_err("%s: Blocking concurrent access\n", __func__);
+ retval = -EBUSY;
+ goto out;
+ }
+
+ memset(typeOfCommand, 0, sizeof(typeOfCommand));
+
+ temp_buf = kstrdup(buf, GFP_KERNEL);
+ if (!temp_buf) {
+ pr_err("%s: memory allocation failed!",
+ __func__);
+ retval = -ENOMEM;
+ goto unlock;
+ }
+
+ p = temp_buf;
+
+ /* Parse the input string to retrieve 2 hex-digit width cmds/args
+ * separated by one or more spaces.
+ * Any input not equal to 2 hex-digit width are ignored.
+ * A single 2 hex-digit width command w/ or w/o space is allowed.
+ * Inputs not in the valid hex range are also ignored.
+ * In case of encountering any of the above failure, the entire input
+ * buffer is discarded.
+ */
+ while (p && (n < CMD_STR_LEN)) {
+
+ while (isspace(*p)) {
+ p++;
+ }
+
+ token = strsep(&p, " ");
+
+ if (!token || *token == '\0') {
+ break;
+ }
+
+ token_len = strlen(token);
+
+ /* handle last token case */
+ if (token_len == 3 && token[2] == '\n')
+ token[2] = '\0';
+ else if (token_len != 2) {
+ pr_err("%s: bad len. len=%zu\n",
+ __func__, token_len);
+ n = 0;
+ break;
+ }
+
+ if (kstrtou8(token, 16, &result)) {
+ /* Conversion failed due to bad input.
+ * Discard the entire buffer.
+ */
+ pr_err("%s: bad input\n", __func__);
+ n = 0;
+ break;
+ }
+
+ /* found a valid cmd/args */
+ typeOfCommand[n] = result;
+ pr_info("%s: typeOfCommand[%d]=%02X\n",
+ __func__, n, typeOfCommand[n]);
+
+ n++;
+ }
+
+ if (n == 0) {
+ pr_err("%s: Found invalid cmd/arg\n", __func__);
+ retval = -EINVAL;
+ }
+
+ numberParameters = n;
+ pr_info("%s: Number of Parameters = %d\n", __func__, numberParameters);
+
+ kfree(temp_buf);
+
+unlock:
+ mutex_unlock(&info->diag_cmd_lock);
+out:
+ return retval;
+}
+
+static ssize_t stm_fts_cmd_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int res, j, doClean = 0, index = 0;
+ int size = (6 * 2) + 1;
+ int nodes = 0;
+ int init_type = SPECIAL_PANEL_INIT;
+ u8 *all_strbuff = buf;
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ const char *limits_file = info->board->limits_name;
+
+ MutualSenseData compData;
+ SelfSenseData comData;
+ MutualSenseFrame frameMS;
+ SelfSenseFrame frameSS;
+
+ u8 report = 0;
+
+ if (!info) {
+ pr_err("%s: Unable to access driver data\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!mutex_trylock(&info->diag_cmd_lock)) {
+ pr_err("%s: Blocking concurrent access\n", __func__);
+ return -EBUSY;
+ }
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ scnprintf(buf, PAGE_SIZE, "{ %08X }\n", res);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ mutex_unlock(&info->diag_cmd_lock);
+ return 0;
+ }
+
+ if (numberParameters >= 1) {
+ res = fts_enableInterrupt(false);
+ if (res < 0) {
+ pr_err("fts_enableInterrupt: ERROR %08X\n", res);
+ res = (res | ERROR_DISABLE_INTER);
+ goto END;
+ }
+
+ switch (typeOfCommand[0]) {
+ /*ITO TEST*/
+ case 0x01:
+ frameMS.node_data = NULL;
+ res = production_test_ito(limits_file, &tests,
+ &frameMS);
+ /* report MS raw frame only if was successfully
+ * acquired */
+ if (frameMS.node_data != NULL) {
+ size += (frameMS.node_data_size *
+ sizeof(short) + 2) * 2;
+ report = 1;
+ }
+ break;
+
+ /*PRODUCTION TEST*/
+ case 0x02:
+ if (systemInfo.u8_cfgAfeVer != systemInfo.u8_cxAfeVer) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("Miss match in CX version! MP test not allowed with wrong CX memory! ERROR %08X\n",
+ res);
+ break;
+ }
+ res = production_test_initialization(init_type);
+ break;
+
+ case 0x00:
+#ifndef COMPUTE_INIT_METHOD
+ if (systemInfo.u8_cfgAfeVer != systemInfo.u8_cxAfeVer) {
+ res = ERROR_OP_NOT_ALLOW;
+ pr_err("Miss match in CX version! MP test not allowed with wrong CX memory! ERROR %08X\n",
+ res);
+ break;
+ }
+#else
+ if (systemInfo.u8_mpFlag != MP_FLAG_FACTORY) {
+ init_type = SPECIAL_FULL_PANEL_INIT;
+ pr_info("Select Full Panel Init!\n");
+ } else {
+ init_type = NO_INIT;
+ pr_info("Skip Full Panel Init!\n");
+ }
+#endif
+ res = production_test_main(limits_file, 1, init_type,
+ &tests, MP_FLAG_FACTORY);
+ break;
+
+ /*read mutual raw*/
+ case 0x13:
+ pr_info("Get 1 MS Frame\n");
+ if (numberParameters >= 2 &&
+ typeOfCommand[1] == LOCKED_LP_ACTIVE)
+ setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_ACTIVE);
+ else
+ setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ /* Skip sensing off when typeOfCommand[2]=0x01
+ * to avoid sense on force cal after reading raw data
+ */
+ if (!(numberParameters >= 3 &&
+ typeOfCommand[2] == 0x01)) {
+ setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+ /* Delete the events related to some touch
+ * (allow to call this function while touching
+ * the screen without having a flooding of the
+ * FIFO)
+ */
+ flushFIFO();
+ }
+#ifdef READ_FILTERED_RAW
+ res = getMSFrame3(MS_FILTER, &frameMS);
+#else
+ res = getMSFrame3(MS_RAW, &frameMS);
+#endif
+ if (res < 0) {
+ pr_err("Error while taking the MS frame... ERROR %08X\n",
+ res);
+ } else {
+ pr_info("The frame size is %d words\n",
+ res);
+#ifdef RAW_DATA_FORMAT_DEC
+ size += 3 * 2 +
+ (7 * frameMS.header.sense_node + 1)
+ * frameMS.header.force_node;
+#else
+ size += (res * sizeof(short) + 2) * 2;
+#endif
+ /* 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:
+ pr_info("Get 1 SS Frame\n");
+ if (numberParameters >= 2 &&
+ typeOfCommand[1] == LOCKED_LP_DETECT)
+ setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_DETECT);
+ else
+ setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ /* Skip sensing off when typeOfCommand[2]=0x01
+ * to avoid sense on force cal after reading raw data
+ */
+ if (!(numberParameters >= 3 &&
+ typeOfCommand[2] == 0x01)) {
+ setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+ flushFIFO();
+ /* delete the events related to some touch
+ * (allow to call this function while touching
+ * the screen without having a flooding of the
+ * FIFO)
+ */
+ }
+ if (numberParameters >= 2 &&
+ typeOfCommand[1] == LOCKED_LP_DETECT)
+#ifdef READ_FILTERED_RAW
+ res = getSSFrame3(SS_DETECT_FILTER, &frameSS);
+#else
+ res = getSSFrame3(SS_DETECT_RAW, &frameSS);
+#endif
+ else
+#ifdef READ_FILTERED_RAW
+ res = getSSFrame3(SS_FILTER, &frameSS);
+#else
+ res = getSSFrame3(SS_RAW, &frameSS);
+#endif
+ if (res < OK) {
+ pr_err("Error while taking the SS frame... ERROR %08X\n",
+ res);
+ } else {
+ pr_info("The frame size is %d words\n", res);
+#ifdef RAW_DATA_FORMAT_DEC
+ size += 3 * 2 + 5 +
+ (frameSS.header.sense_node +
+ frameSS.header.force_node) * 7;
+#else
+ size += (res * sizeof(short) + 2) * 2;
+#endif
+ /* 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 0x14: /* read mutual comp data */
+ pr_info("Get MS Compensation Data\n");
+ res = readMutualSenseCompensationData(LOAD_CX_MS_TOUCH,
+ &compData);
+
+ if (res < 0)
+ pr_err("Error reading MS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("MS Compensation Data Reading Finished!\n");
+ size += ((compData.node_data_size + 3) *
+ sizeof(u8)) * 2;
+ print_frame_i8("MS Data (Cx2) =",
+ array1dTo2d_i8(
+ compData.node_data,
+ compData.
+ node_data_size,
+ compData.header.
+ sense_node),
+ compData.header.force_node,
+ compData.header.sense_node);
+ }
+ break;
+
+ case 0x16: /* read self comp data */
+ pr_info("Get SS Compensation Data...\n");
+ res = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH,
+ &comData);
+ if (res < 0)
+ pr_err("Error reading SS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("SS Compensation Data Reading Finished!\n");
+ size += ((comData.header.force_node +
+ comData.header.sense_node) * 2 + 8) *
+ 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_i8("SS Data Cx2_fm = ",
+ array1dTo2d_i8(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_i8("SS Data Cx2_sn = ",
+ array1dTo2d_i8(comData.cx2_sn,
+ comData.header.
+ sense_node,
+ comData.header.
+ sense_node), 1,
+ comData.header.sense_node);
+ }
+ break;
+ case 0x17: /* Read mutual strength */
+ pr_info("Get 1 MS Strength\n");
+ /* Skip sensing off when typeOfCommand[1]=0x01
+ * to avoid sense on force cal after reading raw data
+ */
+ if (!(numberParameters >= 2 &&
+ typeOfCommand[1] == 0x01)) {
+ setScanMode(SCAN_MODE_ACTIVE, 0xFF);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+ /* Flush outstanding touch events */
+ flushFIFO();
+ }
+ nodes = getMSFrame3(MS_STRENGTH, &frameMS);
+ if (nodes < 0) {
+ res = nodes;
+ pr_err("Error while taking the MS strength... ERROR %08X\n",
+ res);
+ } else {
+ pr_info("The frame size is %d words\n", nodes);
+#ifdef RAW_DATA_FORMAT_DEC
+ size += 3 * 2 +
+ (7 * frameMS.header.sense_node + 1)
+ * frameMS.header.force_node;
+#else
+ size += (nodes * sizeof(short) + 2) * 2;
+#endif
+ print_frame_short("MS strength =",
+ array1dTo2d_short(frameMS.node_data,
+ frameMS.node_data_size,
+ frameMS.header.sense_node),
+ frameMS.header.force_node,
+ frameMS.header.sense_node);
+ res = OK;
+ }
+ break;
+ case 0x03: /* MS Raw DATA TEST */
+ res = fts_system_reset();
+ if (res >= OK)
+ res = production_test_ms_raw(limits_file, 1,
+ &tests);
+ break;
+
+ case 0x04: /* MS CX DATA TEST */
+ res = fts_system_reset();
+ if (res >= OK)
+ res = production_test_ms_cx(limits_file, 1,
+ &tests);
+ break;
+
+ case 0x05: /* SS RAW DATA TEST */
+ res = fts_system_reset();
+ if (res >= OK)
+ res = production_test_ss_raw(limits_file, 1,
+ &tests);
+ break;
+
+ case 0x06: /* SS IX CX DATA TEST */
+ res = fts_system_reset();
+ if (res >= OK)
+ res = production_test_ss_ix_cx(limits_file, 1,
+ &tests);
+ break;
+
+
+ case 0xF0:
+ case 0xF1: /* TOUCH ENABLE/DISABLE */
+ doClean = (int)(typeOfCommand[0] & 0x01);
+ res = cleanUp(doClean);
+ break;
+
+ default:
+ pr_err("COMMAND NOT VALID!! Insert a proper value ...\n");
+ res = ERROR_OP_NOT_ALLOW;
+ break;
+ }
+
+ doClean = fts_mode_handler(info, 1);
+ if (typeOfCommand[0] != 0xF0)
+ doClean |= fts_enableInterrupt(true);
+ if (doClean < 0)
+ pr_err("%s: ERROR %08X\n", __func__,
+ (doClean | ERROR_ENABLE_INTER));
+ } else {
+ pr_err("NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+END:
+ /* here start the reporting phase, assembling the data
+ * to send in the file node */
+ size = PAGE_SIZE;
+ index = 0;
+ index += scnprintf(all_strbuff + index, size - index, "{ %08X", res);
+
+ if (res >= OK || report) {
+ /*all the other cases are already fine printing only the res.*/
+ switch (typeOfCommand[0]) {
+ case 0x01:
+ case 0x13:
+ case 0x17:
+
+ if (frameMS.node_data == NULL)
+ break;
+
+#ifdef RAW_DATA_FORMAT_DEC
+ index += scnprintf(all_strbuff + index, size - index,
+ "%3d",
+ (u8)frameMS.header.force_node);
+ index += scnprintf(all_strbuff + index, size - index,
+ "%3d",
+ (u8)frameMS.header.sense_node);
+#else
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)frameMS.header.force_node);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)frameMS.header.sense_node);
+#endif
+
+ for (j = 0; j < frameMS.node_data_size; j++) {
+#ifdef RAW_DATA_FORMAT_DEC
+ if (j % frameMS.header.sense_node == 0)
+ index += scnprintf(all_strbuff + index,
+ size - index, "\n");
+ index += scnprintf(all_strbuff + index,
+ size - index, "%d ",
+ frameMS.node_data[j]);
+#else
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X%02X",
+ (frameMS.node_data[j] & 0xFF00) >> 8,
+ frameMS.node_data[j] & 0xFF);
+#endif
+ }
+
+ kfree(frameMS.node_data);
+ break;
+
+ case 0x15:
+#ifdef RAW_DATA_FORMAT_DEC
+ index += scnprintf(all_strbuff + index, size - index,
+ "%3d",
+ (u8)frameSS.header.force_node);
+ index += scnprintf(all_strbuff + index, size - index,
+ "%3d",
+ (u8)frameSS.header.sense_node);
+ index += scnprintf(all_strbuff + index, size - index,
+ "\n");
+#else
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)frameSS.header.force_node);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)frameSS.header.sense_node);
+#endif
+
+ /* Copying self raw data Force */
+ for (j = 0; j < frameSS.header.force_node; j++) {
+#ifdef RAW_DATA_FORMAT_DEC
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%d ",
+ frameSS.force_data[j]);
+#else
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X%02X",
+ (frameSS.force_data[j] & 0xFF00) >> 8,
+ frameSS.force_data[j] & 0xFF);
+#endif
+ }
+
+
+
+#ifdef RAW_DATA_FORMAT_DEC
+ index += scnprintf(all_strbuff + index, size - index,
+ "\n");
+#endif
+
+ /* Copying self raw data Sense */
+ for (j = 0; j < frameSS.header.sense_node; j++) {
+#ifdef RAW_DATA_FORMAT_DEC
+ index += scnprintf(all_strbuff + index,
+ size - index, "%d ",
+ frameSS.sense_data[j]);
+#else
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X%02X",
+ (frameSS.sense_data[j] & 0xFF00) >> 8,
+ frameSS.sense_data[j] & 0xFF);
+#endif
+ }
+
+ kfree(frameSS.force_data);
+ kfree(frameSS.sense_data);
+ break;
+
+ case 0x14:
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)compData.header.force_node);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (u8)compData.header.sense_node);
+
+ /* Cpying CX1 value */
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (compData.cx1) & 0xFF);
+
+ /* Copying CX2 values */
+ for (j = 0; j < compData.node_data_size; j++) {
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X",
+ (compData.node_data[j]) & 0xFF);
+ }
+
+ kfree(compData.node_data);
+ break;
+
+ case 0x16:
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ comData.header.force_node);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ comData.header.sense_node);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.f_ix1) & 0xFF);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.s_ix1) & 0xFF);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.f_cx1) & 0xFF);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.s_cx1) & 0xFF);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.f_ix0) & 0xFF);
+
+ index += scnprintf(all_strbuff + index,
+ size - index, "%02X",
+ (comData.s_ix0) & 0xFF);
+
+ /* Copying IX2 Force */
+ for (j = 0; j < comData.header.force_node; j++) {
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X",
+ comData.ix2_fm[j] & 0xFF);
+ }
+
+ /* Copying IX2 Sense */
+ for (j = 0; j < comData.header.sense_node; j++) {
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X",
+ comData.ix2_sn[j] & 0xFF);
+ }
+
+ /* Copying CX2 Force */
+ for (j = 0; j < comData.header.force_node; j++) {
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X",
+ comData.cx2_fm[j] & 0xFF);
+ }
+
+ /* Copying CX2 Sense */
+ for (j = 0; j < comData.header.sense_node; j++) {
+ index += scnprintf(all_strbuff + index,
+ size - index,
+ "%02X",
+ comData.cx2_sn[j] & 0xFF);
+ }
+
+ kfree(comData.ix2_fm);
+ kfree(comData.ix2_sn);
+ kfree(comData.cx2_fm);
+ kfree(comData.cx2_sn);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ index += scnprintf(all_strbuff + index, size - index, " }\n");
+ numberParameters = 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 */
+ /* pr_err("numberParameters = %d\n", numberParameters); */
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ mutex_unlock(&info->diag_cmd_lock);
+
+ return index;
+}
+
+static ssize_t fts_autotune_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+ bool val = false;
+
+ if ((kstrtobool(buf, &val) < 0) || !val) {
+ ret = -EINVAL;
+ goto err_args;
+ }
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true);
+
+ ret = production_test_main(info->board->limits_name, 1,
+ SPECIAL_FULL_PANEL_INIT, &tests,
+ MP_FLAG_BOOT);
+
+ cleanUp(true);
+
+ info->autotune_stat = ret;
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+
+err_args:
+
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t fts_autotune_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "{ %08X }\n", info->autotune_stat);
+}
+
+static ssize_t fts_infoblock_getdata_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int count = 0;
+ int res = 0;
+ u8 control_reg = 0;
+ u8 flash_status = 0;
+ u8 *data = NULL;
+ int addr = 0;
+ int i = 0;
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true);
+
+ res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG,
+ ADDR_FLASH_STATUS, &control_reg,
+ 1, DUMMY_HW_REG);
+
+ if (res < OK) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "ADDR_FLASH_STATUS read failed\n");
+ goto END;
+ }
+ flash_status = (control_reg & 0xFC);
+
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "The value:0x%X 0x%X\n", control_reg, flash_status);
+
+ res = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_FLASH_STATUS, &flash_status, 1);
+ if (res < OK) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "ADDR_FLASH_STATUS write failed\n");
+ goto END;
+ }
+ data = kmalloc(INFO_BLOCK_SIZE * sizeof(u8), GFP_KERNEL);
+ if (data == NULL) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "kmalloc failed\n");
+ goto END;
+ }
+ res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG,
+ ADDR_INFOBLOCK, data, INFO_BLOCK_SIZE,
+ DUMMY_HW_REG);
+ if (res < OK) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "ADDR_INFOBLOCK read failed\n");
+ goto END;
+ }
+ addr = INFO_BLOCK_LOCKDOWN;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Lock down info the first 4bytes:0X%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Lock down info the second 4bytes:0X%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "0x%04X\n", addr);
+ addr = INFO_BLOCK_AOFFSET;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset magic number:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset crc:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset ~crcr:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset len:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset ~len:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset ver:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "0x%04X\n", addr);
+ for (i = 0; i < 38; i++) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset CH[%d] Quar:0X%02X,Half:0X%02X,Full:0X%02X%02X\n",
+ i, data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ }
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "0x%04X\n", addr);
+ for (i = 0; i < 4; i++) {
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "Aoffset CA[%d] Quar:0X%02X,Half:0X%02X,Full:0X%02X%02X\n",
+ i, data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ }
+ addr = INFO_BLOCK_OSC;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim magic number:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim crc:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim len:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim ~crcr:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim ~len:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim ver:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim major ver:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim cen bg:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim frequency bg:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim frequency afe:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim cen bg valid:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+ addr += 4;
+ count += scnprintf(&buf[count], PAGE_SIZE - count,
+ "OscTrim cen afe valid:0x%02X%02X%02X%02X\n",
+ data[addr+3], data[addr+2], data[addr+1],
+ data[addr]);
+
+END:
+ kfree(data);
+
+ if (control_reg != flash_status)
+ fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_FLASH_STATUS, &control_reg, 1);
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+
+ return count;
+}
+
+/* sysfs file node to store heatmap mode
+ * "echo cmd > heatmap_mode" to change
+ * Possible commands:
+ * 0 = FTS_HEATMAP_OFF
+ * 1 = FTS_HEATMAP_PARTIAL
+ * 2 = FTS_HEATMAP_FULL
+ */
+#ifdef TOUCHSCREEN_HEATMAP
+static ssize_t fts_heatmap_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int result;
+ int val;
+
+ result = kstrtoint(buf, 10, &val);
+ if (result < 0 || val < FTS_HEATMAP_OFF || val > FTS_HEATMAP_FULL) {
+ pr_err("%s: Invalid input.\n", __func__);
+ return -EINVAL;
+ }
+
+ info->heatmap_mode = val;
+ return count;
+}
+
+static ssize_t fts_heatmap_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ info->heatmap_mode);
+}
+#endif
+
+static DEVICE_ATTR(infoblock_getdata, (0444),
+ fts_infoblock_getdata_show, NULL);
+static DEVICE_ATTR(fwupdate, 0664, fts_fwupdate_show,
+ fts_fwupdate_store);
+static DEVICE_ATTR(appid, 0444, fts_appid_show, NULL);
+static DEVICE_ATTR(mode_active, 0444, fts_mode_active_show, NULL);
+static DEVICE_ATTR(fw_file_test, 0444, fts_fw_test_show, NULL);
+static DEVICE_ATTR(status, 0444, fts_status_show, NULL);
+static DEVICE_ATTR(stm_fts_cmd, 0664, stm_fts_cmd_show,
+ stm_fts_cmd_store);
+#ifdef TOUCHSCREEN_HEATMAP
+static DEVICE_ATTR(heatmap_mode, 0664, fts_heatmap_mode_show,
+ fts_heatmap_mode_store);
+#endif
+#ifdef USE_ONE_FILE_NODE
+static DEVICE_ATTR(feature_enable, 0664,
+ fts_feature_enable_show, fts_feature_enable_store);
+#else
+
+
+#ifdef GRIP_MODE
+static DEVICE_ATTR(grip_mode, 0664, fts_grip_mode_show,
+ fts_grip_mode_store);
+#endif
+
+#ifdef CHARGER_MODE
+static DEVICE_ATTR(charger_mode, 0664,
+ fts_charger_mode_show, fts_charger_mode_store);
+#endif
+
+#ifdef GLOVE_MODE
+static DEVICE_ATTR(glove_mode, 0664,
+ fts_glove_mode_show, fts_glove_mode_store);
+#endif
+
+#ifdef COVER_MODE
+static DEVICE_ATTR(cover_mode, 0664,
+ fts_cover_mode_show, fts_cover_mode_store);
+#endif
+
+#ifdef STYLUS_MODE
+static DEVICE_ATTR(stylus_mode, 0664,
+ fts_stylus_mode_show, fts_stylus_mode_store);
+#endif
+
+#endif
+
+#ifdef GESTURE_MODE
+static DEVICE_ATTR(gesture_mask, 0664,
+ fts_gesture_mask_show, fts_gesture_mask_store);
+static DEVICE_ATTR(gesture_coordinates, 0664,
+ fts_gesture_coordinates_show, NULL);
+#endif
+static DEVICE_ATTR(autotune, 0664, fts_autotune_show, fts_autotune_store);
+
+static DEVICE_ATTR(touchsim, 0664,
+ fts_touch_simulation_show,
+ fts_touch_simulation_store);
+
+static DEVICE_ATTR(default_mf, 0664,
+ fts_default_mf_show,
+ fts_default_mf_store);
+
+#ifdef SUPPORT_PROX_PALM
+static DEVICE_ATTR(audio_status, 0644,
+ audio_status_show,
+ audio_status_store);
+
+static DEVICE_ATTR(prox_palm_status, 0644,
+ prox_palm_status_show,
+ prox_palm_status_store);
+#endif
+
+/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */
+static struct attribute *fts_attr_group[] = {
+ &dev_attr_infoblock_getdata.attr,
+ &dev_attr_fwupdate.attr,
+ &dev_attr_appid.attr,
+ &dev_attr_mode_active.attr,
+ &dev_attr_fw_file_test.attr,
+ &dev_attr_status.attr,
+ &dev_attr_stm_fts_cmd.attr,
+#ifdef TOUCHSCREEN_HEATMAP
+ &dev_attr_heatmap_mode.attr,
+#endif
+#ifdef USE_ONE_FILE_NODE
+ &dev_attr_feature_enable.attr,
+#else
+
+#ifdef GRIP_MODE
+ &dev_attr_grip_mode.attr,
+#endif
+#ifdef CHARGER_MODE
+ &dev_attr_charger_mode.attr,
+#endif
+#ifdef GLOVE_MODE
+ &dev_attr_glove_mode.attr,
+#endif
+#ifdef COVER_MODE
+ &dev_attr_cover_mode.attr,
+#endif
+#ifdef STYLUS_MODE
+ &dev_attr_stylus_mode.attr,
+#endif
+
+#endif
+
+#ifdef GESTURE_MODE
+ &dev_attr_gesture_mask.attr,
+ &dev_attr_gesture_coordinates.attr,
+#endif
+ &dev_attr_autotune.attr,
+ &dev_attr_touchsim.attr,
+ &dev_attr_default_mf.attr,
+#ifdef SUPPORT_PROX_PALM
+ &dev_attr_prox_palm_status.attr,
+ &dev_attr_audio_status.attr,
+#endif
+ NULL,
+};
+
+#ifdef SUPPORT_PROX_PALM
+/* sysfs file node to store audio status
+ * "echo cmd > audio_status" to change
+ */
+static ssize_t audio_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int result;
+ int val;
+
+ result = kstrtoint(buf, 10, &val);
+ if (result < 0) {
+ pr_err("%s: Invalid input.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (val == 0) {
+ fts_set_bus_ref(info, FTS_BUS_REF_PHONE_CALL, false);
+ } else {
+ fts_set_bus_ref(info, FTS_BUS_REF_PHONE_CALL, true);
+ sysfs_notify(&dev->kobj, NULL, dev_attr_prox_palm_status.attr.name);
+ }
+ info->audio_status = val;
+ pr_info("%s: audio status %d", __func__, val);
+ return count;
+}
+
+static ssize_t audio_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ info->audio_status);
+}
+
+static ssize_t prox_palm_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ int result;
+ int val;
+
+ result = kstrtoint(buf, 10, &val);
+ if (result < 0) {
+ pr_err("%s: Invalid input.\n", __func__);
+ return -EINVAL;
+ }
+
+ info->prox_palm_status = val;
+ sysfs_notify(&dev->kobj, NULL, dev_attr_prox_palm_status.attr.name);
+ pr_info("%s Notify prox_palms status %d", __func__,
+ info->prox_palm_status);
+ return count;
+}
+
+static ssize_t prox_palm_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ info->prox_palm_status);
+}
+
+/**
+ * When the feature is enabled, touch IC will keep in active sensing mode
+ * but only report status event and gesture event and stop reporting
+ * pointer location event
+ */
+int enable_prox_palm_only_mode(bool enable)
+{
+ u8 cmd[3] = {0xC0, 0x0D, (u8) enable};
+ int ret = 0;
+
+ ret = fts_write(cmd, 3);
+ if (ret < 0)
+ pr_err("%s: %s failed, ret = %d", __func__,
+ enable ? "enable" : "disable", ret);
+ return ret;
+}
+#endif
+/** @}*/
+/** @}*/
+
+
+/**
+ * @defgroup isr Interrupt Service Routine (Event Handler)
+ * The most important part of the driver is the ISR (Interrupt Service Routine)
+ * called also as Event Handler \n
+ * As soon as the interrupt pin goes low, fts_interrupt_handler() is called and
+ * the chain to read and parse the event read from the FIFO start.\n
+ * For any different kind of EVT_ID there is a specific event handler
+ * which will take the correct action to report the proper info to the host. \n
+ * The most important events are the one related to touch information, status
+ * update or user report.
+ * @{
+ */
+
+/**
+ * Report to the linux input system the pressure and release of a button
+ * handling concurrency
+ * @param info pointer to fts_ts_info which contains info about the device
+ * and its hw setup
+ * @param key_code button value
+ */
+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);
+}
+
+
+
+/**
+ * Event Handler for no events (EVT_ID_NOEVENT)
+ */
+static bool fts_nop_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ pr_info("%s: Doing nothing for event = %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3],
+ event[4],
+ event[5], event[6], event[7]);
+ return false;
+}
+
+/**
+ * Event handler for enter and motion events (EVT_ID_ENTER_POINT,
+ * EVT_ID_MOTION_POINT )
+ * report touch coordinates and additional information
+ * to the linux input system
+ */
+static bool fts_enter_pointer_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ unsigned char touchId;
+ unsigned int touch_condition = 1, tool = MT_TOOL_FINGER;
+ int x, y, z, major, minor, distance;
+ u8 touchType;
+
+ if (!info->resume_bit)
+ goto no_report;
+
+ touchType = event[1] & 0x0F;
+ touchId = (event[1] & 0xF0) >> 4;
+
+ x = (((int)event[3] & 0x0F) << 8) | (event[2]);
+ y = ((int)event[4] << 4) | ((event[3] & 0xF0) >> 4);
+ z = (int)event[5];
+ if (z <= 0) {
+ /* Should not happen, because zero pressure implies contact has
+ * left, so this function should not be invoked. For safety, to
+ * prevent this touch from being dropped, set to smallest
+ * pressure value instead
+ */
+#ifndef SKIP_PRESSURE
+ pr_debug("%s: Pressure is %i, but pointer is not leaving\n",
+ __func__, z);
+#endif
+ z = 1; /* smallest non-zero pressure value */
+ }
+ major = (int)(((event[0] & 0x0C) << 2) | ((event[6] & 0xF0) >> 4));
+ minor = (int)(((event[7] & 0xC0) >> 2) | (event[6] & 0x0F));
+ /* TODO: check with fw how they will report distance */
+ distance = 0; /* if the tool is touching the display
+ * the distance should be 0 */
+
+ if (x > info->board->x_axis_max)
+ x = info->board->x_axis_max;
+
+ if (y > info->board->y_axis_max)
+ y = info->board->y_axis_max;
+
+ input_mt_slot(info->input_dev, touchId);
+ switch (touchType) {
+#ifdef STYLUS_MODE
+ case TOUCH_TYPE_STYLUS:
+ pr_info("%s : It is a stylus!\n", __func__);
+ if (info->stylus_enabled == 1) {
+ /* if stylus_enabled is not ==1
+ * it will be reported as normal touch */
+ tool = MT_TOOL_PEN;
+ touch_condition = 1;
+ __set_bit(touchId, &info->stylus_id);
+ break;
+ }
+#endif
+ /* TODO: customer can implement a different strategy for each kind of
+ * touch */
+ case TOUCH_TYPE_FINGER:
+ case TOUCH_TYPE_GLOVE:
+ pr_debug("%s : It is a touch type %d!\n", __func__, touchType);
+ if (info->palm_touch_mask)
+ tool = MT_TOOL_PALM;
+ else
+ tool = MT_TOOL_FINGER;
+ touch_condition = 1;
+ __set_bit(touchId, &info->touch_id);
+ __clear_bit(touchId, &info->palm_touch_mask);
+ __clear_bit(touchId, &info->grip_touch_mask);
+ break;
+ case TOUCH_TYPE_PALM:
+ pr_debug("%s : It is a touch type %d!\n", __func__, touchType);
+ tool = MT_TOOL_PALM;
+ touch_condition = 1;
+ __set_bit(touchId, &info->touch_id);
+ __set_bit(touchId, &info->palm_touch_mask);
+ __clear_bit(touchId, &info->grip_touch_mask);
+ break;
+ case TOUCH_TYPE_GRIP:
+ pr_debug("%s : It is a touch type %d!\n", __func__, touchType);
+ tool = MT_TOOL_PALM;
+ touch_condition = 1;
+ __set_bit(touchId, &info->touch_id);
+ __clear_bit(touchId, &info->palm_touch_mask);
+ __set_bit(touchId, &info->grip_touch_mask);
+ break;
+
+ case TOUCH_TYPE_HOVER:
+ tool = MT_TOOL_FINGER;
+ touch_condition = 0; /* need to hover */
+ z = 0; /* no pressure */
+ __set_bit(touchId, &info->touch_id);
+ distance = DISTANCE_MAX;/* check with fw report the hovering
+ * distance */
+ break;
+
+ default:
+ pr_err("%s : Invalid touch type = %d ! No Report...\n",
+ __func__, touchType);
+ goto no_report;
+ }
+
+ input_report_key(info->input_dev, BTN_TOUCH, touch_condition);
+ input_mt_report_slot_state(info->input_dev, tool, 1);
+
+ /* pr_info("%s : TouchID = %d,Touchcount = %d\n", __func__,
+ * touchId,touchcount); */
+
+ 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);
+#ifndef SKIP_PRESSURE
+ input_report_abs(info->input_dev, ABS_MT_PRESSURE, z);
+#endif
+
+#ifndef SKIP_DISTANCE
+ input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance);
+#endif
+ /* pr_info("%s : Event 0x%02x - ID[%d], (x, y) = (%3d, %3d)
+ * Size = %d\n",
+ * __func__, *event, touchId, x, y, touchType); */
+
+ return true;
+no_report:
+ return false;
+}
+
+/**
+ * Event handler for leave event (EVT_ID_LEAVE_POINT )
+ * Report to the linux input system that one touch left the display
+ */
+static bool fts_leave_pointer_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ unsigned char touchId;
+ unsigned int tool = MT_TOOL_FINGER;
+ u8 touchType;
+
+ touchType = event[1] & 0x0F;
+ touchId = (event[1] & 0xF0) >> 4;
+
+ input_mt_slot(info->input_dev, touchId);
+
+ input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
+ switch (touchType) {
+#ifdef STYLUS_MODE
+ case TOUCH_TYPE_STYLUS:
+ pr_info("%s : It is a stylus!\n", __func__);
+ if (info->stylus_enabled == 1) {
+ /* if stylus_enabled is not ==1 it will be reported as
+ * normal touch */
+ tool = MT_TOOL_PEN;
+ __clear_bit(touchId, &info->stylus_id);
+ break;
+ }
+#endif
+
+ case TOUCH_TYPE_FINGER:
+ /* pr_info("%s : It is a finger!\n", __func__); */
+ case TOUCH_TYPE_GLOVE:
+ /* pr_info("%s : It is a glove!\n", __func__); */
+ case TOUCH_TYPE_PALM:
+ /* pr_info("%s : It is a palm!\n", __func__); */
+ case TOUCH_TYPE_GRIP:
+ /* pr_info("%s : It is a grip!\n", __func__); */
+ case TOUCH_TYPE_HOVER:
+ tool = MT_TOOL_FINGER;
+ __clear_bit(touchId, &info->touch_id);
+ __clear_bit(touchId, &info->palm_touch_mask);
+ __clear_bit(touchId, &info->grip_touch_mask);
+ break;
+
+ default:
+ pr_err("%s : Invalid touch type = %d ! No Report...\n",
+ __func__, touchType);
+ return false;
+ }
+
+ input_mt_report_slot_state(info->input_dev, tool, 0);
+
+ /* pr_info("%s : TouchID = %d, Touchcount = %d\n", __func__,
+ * touchId,touchcount); */
+
+
+ input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1);
+ /* pr_info("%s : Event 0x%02x - release ID[%d]\n", __func__,
+ * event[0], touchId); */
+ return true;
+}
+
+/* EventId : EVT_ID_MOTION_POINT */
+#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler
+/* remap the motion event handler to the same function which handle the enter
+ * event */
+
+/**
+ * Event handler for error events (EVT_ID_ERROR)
+ * Handle unexpected error events implementing recovery strategy and
+ * restoring the sensing status that the IC had before the error occurred
+ */
+static bool fts_error_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ int error = 0;
+
+ pr_info("%s: Received event %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3], event[4],
+ event[5],
+ event[6], event[7]);
+
+ switch (event[1]) {
+ case EVT_TYPE_ERROR_ESD:/* 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(true);
+ if (error < OK)
+ pr_err("%s Cannot restore the device ERROR %08X\n",
+ __func__, error);
+ }
+ break;
+ case EVT_TYPE_ERROR_HARD_FAULT: /* hard fault */
+ case EVT_TYPE_ERROR_WATCHDOG: /* watch dog timer */
+ {
+ dumpErrorInfo(NULL, 0);
+ /* before reset clear all slots */
+ release_all_touches(info);
+ error = fts_system_reset();
+ error |= fts_mode_handler(info, 0);
+ error |= fts_enableInterrupt(true);
+ if (error < OK)
+ pr_err("%s Cannot reset the device ERROR %08X\n",
+ __func__, error);
+ }
+ break;
+ }
+ return false;
+}
+
+/**
+ * Event handler for controller ready event (EVT_ID_CONTROLLER_READY)
+ * Handle controller events received after unexpected reset of the IC updating
+ * the resets flag and restoring the proper sensing status
+ */
+static bool fts_controller_ready_event_handler(struct fts_ts_info *info,
+ unsigned char *event)
+{
+ int error;
+
+ pr_info("%s: Received event %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ release_all_touches(info);
+ setSystemResetedUp(1);
+ setSystemResetedDown(1);
+ error = fts_mode_handler(info, 0);
+ if (error < OK)
+ pr_err("%s Cannot restore the device status ERROR %08X\n",
+ __func__, error);
+ return false;
+}
+
+/**
+ * Event handler for status events (EVT_ID_STATUS_UPDATE)
+ * Handle status update events
+ */
+static bool fts_status_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ u8 grid_touch_status;
+
+ switch (event[1]) {
+ case EVT_TYPE_STATUS_ECHO:
+ pr_debug("%s: Echo event of command = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4], event[5],
+ event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_GPIO_CHAR_DET:
+ pr_info("%s: GPIO Charger Detect ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4], event[5],
+ event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_FORCE_CAL:
+ switch (event[2]) {
+ case 0x01:
+ pr_info("%s: Sense on Force cal = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_info("%s: Host command Force cal = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x10:
+ pr_info("%s: Mutual frame drop Force cal = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x11:
+ pr_info("%s: Mutual pure raw Force cal = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x20:
+ pr_info("%s: Self detect negative Force cal = %02X"
+ " %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x21:
+ pr_info("%s: Self touch negative Force cal = %02X"
+ " %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x22:
+ pr_info("%s: Self detect frame flatness Force cal ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x23:
+ pr_info("%s: Self touch frame flatness Force cal ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x30:
+ pr_info("%s: Invalid mutual Force cal = %02X"
+ " %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x31:
+ pr_info("%s: Invalid differential mutual Force cal ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x32:
+ pr_info("%s: Invalid Self Force cal = %02X"
+ " %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x33:
+ pr_info("%s: Invalid Self island Force cal = %02X"
+ " %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x34:
+ pr_info("%s: Invalid Self force touch Force cal ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x35:
+ pr_info("%s: Mutual frame flatness Force cal ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_info("%s: Unknown force cal = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_FRAME_DROP:
+ pr_info("%s: Frame drop = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_SS_RAW_SAT:
+ if (event[2] == 1)
+ pr_info("%s: SS Raw Saturated = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ else
+ pr_info("%s: SS Raw No more Saturated = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_WATER:
+ switch (event[2]) {
+ case 0x00:
+ pr_info("%s: Water Mode Entry by BLD with real"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x01:
+ pr_info("%s: Water Mode Entry by BLD with rom"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_info("%s: Water Mode Entry by MID with real"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x03:
+ pr_info("%s: Water Mode leave by BLD with real"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x04:
+ pr_info("%s: Water Mode leave by BLD with rom"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x05:
+ pr_info("%s: Water Mode leave by MID with real"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_info("%s: Unknown water mode = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_PRE_WAT_DET:
+ if (event[2] == 1)
+ pr_info("%s: Previous Water entry ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ else
+ pr_info("%s: Previous Water leave ="
+ " %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_NOISE:
+ if(info->scanning_frequency != event[3]) {
+ pr_info("%s: Scanning frequency changed from %02X to %02X\n",
+ __func__, info->scanning_frequency, event[3]);
+ pr_info("%s: Noise Status Event = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3],
+ event[4], event[5], event[6], event[7]);
+ info->scanning_frequency = event[3];
+ } else {
+ pr_debug("%s: Noise Status Event = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_STIMPAD:
+ switch (event[2]) {
+ case 0x00:
+ pr_debug("%s: Stimpad disable event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x01:
+ pr_debug("%s: Stimpad enable event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_debug("%s: Stimpad disable by signature invalid"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x03:
+ pr_debug("%s: Stimpad disable by nodes count invalid"
+ " raw frame = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_debug("%s: Unknown stimpad status = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_NO_TOUCH:
+ pr_info("%s: No Touch Status Event = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4], event[5],
+ event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_IDLE:
+ pr_info("%s: Idle Status Event = %02X %02X"
+ " %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4], event[5],
+ event[6], event[7]);
+ break;
+
+ case EVT_TYPE_STATUS_PALM_TOUCH:
+ switch (event[2]) {
+ case 0x01:
+ pr_info("%s: Palm block entry event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_info("%s: Palm block release event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_info("%s: Unknown palm touch status = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_GRIP_TOUCH:
+ grid_touch_status = (event[2] & 0xF0) >> 4;
+ switch (grid_touch_status) {
+ case 0x01:
+ pr_info("%s: Grip Touch entry event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_info("%s: Grip Touch release event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_info("%s: Unknown grip touch status = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_GOLDEN_RAW_VAL:
+ switch (event[2]) {
+ case 0x01:
+ pr_info("%s: Golden Raw Validation Pass"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ case 0x02:
+ pr_info("%s: Golden Raw Validation Fail"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+ default:
+ pr_info("%s: Unknown golden raw validation status = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+
+ case EVT_TYPE_STATUS_GOLDEN_RAW_ERR:
+ pr_info("%s: Golden Raw Data Abnormal"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ break;
+
+#ifdef SUPPORT_PROX_PALM
+ case EVT_TYPE_STATUS_PROX_PALM:
+ switch (event[2]) {
+ case 0x00:
+ pr_info("%s: Proximity palm release event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ info->prox_palm_status = 0;
+ sysfs_notify(&info->dev->kobj, NULL, dev_attr_prox_palm_status.attr.name);
+ break;
+
+ case 0x01:
+ pr_info("%s: Proximity palm entry event"
+ " = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ info->prox_palm_status = 1;
+ sysfs_notify(&info->dev->kobj, NULL, dev_attr_prox_palm_status.attr.name);
+ break;
+
+ default:
+ pr_info("%s: Unknown proximity palm status = %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+ }
+ break;
+#endif
+ default:
+ pr_info("%s: Received unknown status event = %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3],
+ event[4], event[5], event[6], event[7]);
+ break;
+ }
+ return false;
+}
+
+
+/* key events reported in the user report */
+#ifdef PHONE_KEY
+/* TODO: the customer should handle the events coming from the keys according
+ * his needs
+ * (this is just an sample code that report the click of a button after a
+ * press->release action) */
+/**
+ * Event handler for status events (EVT_TYPE_USER_KEY)
+ * Handle keys update events, the third byte of the event is a bitmask,
+ * if the bit set means that the corresponding key is pressed.
+ */
+static void fts_key_event_handler(struct fts_ts_info *info,
+ unsigned char *event)
+{
+ /* int value; */
+ pr_info("%s: Received event %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3], event[4],
+ event[5], event[6], event[7]);
+
+ if (event[0] == EVT_ID_USER_REPORT && event[1] == EVT_TYPE_USER_KEY) {
+ /* event[2] contain the bitmask of the keys that are actually
+ * pressed */
+
+ if ((event[2] & FTS_KEY_0) == 0 && (key_mask & FTS_KEY_0) > 0) {
+ pr_info("%s: Button HOME pressed and released!\n",
+ __func__);
+ fts_input_report_key(info, KEY_HOMEPAGE);
+ }
+
+ if ((event[2] & FTS_KEY_1) == 0 && (key_mask & FTS_KEY_1) > 0) {
+ pr_info("%s: Button Back pressed and released!\n",
+ __func__);
+ fts_input_report_key(info, KEY_BACK);
+ }
+
+ if ((event[2] & FTS_KEY_2) == 0 && (key_mask & FTS_KEY_2) > 0) {
+ pr_info("%s: Button Menu pressed!\n", __func__);
+ fts_input_report_key(info, KEY_MENU);
+ }
+
+ key_mask = event[2];
+ } else
+ pr_err("%s: Invalid event passed as argument!\n", __func__);
+}
+#endif
+
+/* gesture event must be handled in the user event handler */
+#ifdef GESTURE_MODE
+/* TODO: Customer should implement their own actions in respond of a gesture
+ * event.
+ * This is an example that simply print the gesture received and simulate
+ * the click on a different button for each gesture. */
+/**
+ * Event handler for gesture events (EVT_TYPE_USER_GESTURE)
+ * Handle gesture events and simulate the click on a different button
+ * for any gesture detected (@link gesture_opt Gesture IDs @endlink)
+ */
+static void fts_gesture_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ int value;
+ int needCoords = 0;
+
+ pr_info("gesture event data: %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]);
+
+ if (event[0] == EVT_ID_USER_REPORT && event[1] ==
+ EVT_TYPE_USER_GESTURE) {
+ needCoords = 1;
+ /* default read the coordinates for all gestures excluding
+ * double tap */
+
+ switch (event[2]) {
+ case GEST_ID_DBLTAP:
+ value = KEY_WAKEUP;
+ pr_info("%s: double tap !\n", __func__);
+ needCoords = 0;
+ break;
+
+ case GEST_ID_AT:
+ value = KEY_WWW;
+ pr_info("%s: @ !\n", __func__);
+ break;
+
+ case GEST_ID_C:
+ value = KEY_C;
+ pr_info("%s: C !\n", __func__);
+ break;
+
+ case GEST_ID_E:
+ value = KEY_E;
+ pr_info("%s: e !\n", __func__);
+ break;
+
+ case GEST_ID_F:
+ value = KEY_F;
+ pr_info("%s: F !\n", __func__);
+ break;
+
+ case GEST_ID_L:
+ value = KEY_L;
+ pr_info("%s: L !\n", __func__);
+ break;
+
+ case GEST_ID_M:
+ value = KEY_M;
+ pr_info("%s: M !\n", __func__);
+ break;
+
+ case GEST_ID_O:
+ value = KEY_O;
+ pr_info("%s: O !\n", __func__);
+ break;
+
+ case GEST_ID_S:
+ value = KEY_S;
+ pr_info("%s: S !\n", __func__);
+ break;
+
+ case GEST_ID_V:
+ value = KEY_V;
+ pr_info("%s: V !\n", __func__);
+ break;
+
+ case GEST_ID_W:
+ value = KEY_W;
+ pr_info("%s: W !\n", __func__);
+ break;
+
+ case GEST_ID_Z:
+ value = KEY_Z;
+ pr_info("%s: Z !\n", __func__);
+ break;
+
+ case GEST_ID_RIGHT_1F:
+ value = KEY_RIGHT;
+ pr_info("%s: -> !\n", __func__);
+ break;
+
+ case GEST_ID_LEFT_1F:
+ value = KEY_LEFT;
+ pr_info("%s: <- !\n", __func__);
+ break;
+
+ case GEST_ID_UP_1F:
+ value = KEY_UP;
+ pr_info("%s: UP !\n", __func__);
+ break;
+
+ case GEST_ID_DOWN_1F:
+ value = KEY_DOWN;
+ pr_info("%s: DOWN !\n", __func__);
+ break;
+
+ case GEST_ID_CARET:
+ value = KEY_APOSTROPHE;
+ pr_info("%s: ^ !\n", __func__);
+ break;
+
+ case GEST_ID_LEFTBRACE:
+ value = KEY_LEFTBRACE;
+ pr_info("%s: < !\n", __func__);
+ break;
+
+ case GEST_ID_RIGHTBRACE:
+ value = KEY_RIGHTBRACE;
+ pr_info("%s: > !\n", __func__);
+ break;
+
+ default:
+ pr_err("%s: No valid GestureID!\n", __func__);
+ goto gesture_done;
+ }
+
+ if (needCoords == 1)
+ readGestureCoords(event);
+
+ fts_input_report_key(info, value);
+
+gesture_done:
+ return;
+ } else
+ pr_err("%s: Invalid event passed as argument!\n", __func__);
+}
+#endif
+
+
+/**
+ * Event handler for user report events (EVT_ID_USER_REPORT)
+ * Handle user events reported by the FW due to some interaction triggered
+ * by an external user (press keys, perform gestures, etc.)
+ */
+static bool fts_user_report_event_handler(struct fts_ts_info *info, unsigned
+ char *event)
+{
+ switch (event[1]) {
+#ifdef PHONE_KEY
+ case EVT_TYPE_USER_KEY:
+ fts_key_event_handler(info, event);
+ break;
+#endif
+
+ case EVT_TYPE_USER_PROXIMITY:
+ if (event[2] == 0)
+ pr_err("%s No proximity!\n", __func__);
+ else
+ pr_err("%s Proximity Detected!\n", __func__);
+ break;
+
+#ifdef GESTURE_MODE
+ case EVT_TYPE_USER_GESTURE:
+ fts_gesture_event_handler(info, event);
+ break;
+#endif
+ default:
+ pr_err("%s: Received unhandled user report event = %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ __func__, event[0], event[1], event[2], event[3],
+ event[4], event[5], event[6], event[7]);
+ break;
+ }
+ return false;
+}
+
+#ifdef TOUCHSCREEN_HEATMAP
+static void heatmap_enable(void)
+{
+ u8 command[] = {FTS_CMD_SYSTEM, SYS_CMD_LOAD_DATA,
+ LOCAL_HEATMAP_MODE};
+ pr_info("%s\n", __func__);
+ fts_write(command, ARRAY_SIZE(command));
+}
+
+static bool read_heatmap_raw(struct v4l2_heatmap *v4l2)
+{
+ struct fts_ts_info *info =
+ container_of(v4l2, struct fts_ts_info, v4l2);
+ unsigned int num_elements;
+ /* index for looping through the heatmap buffer read over the bus */
+ unsigned int local_i;
+
+ int result;
+
+ /* old value of the counter, for comparison */
+ static uint16_t counter;
+
+ strength_t heatmap_value;
+ /* final position of the heatmap value in the full heatmap frame */
+ unsigned int frame_i;
+
+ int heatmap_x, heatmap_y;
+ int max_x = v4l2->format.width;
+ int max_y = v4l2->format.height;
+
+ struct heatmap_report report = {0};
+
+ if (info->heatmap_mode == FTS_HEATMAP_PARTIAL) {
+ result = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16,
+ ADDR_FRAMEBUFFER, (uint8_t *)&report,
+ sizeof(report), DUMMY_FRAMEBUFFER);
+ if (result != OK) {
+ pr_err("%s: i2c read failed, fts_writeRead returned %i",
+ __func__, result);
+ return false;
+ }
+ if (report.mode != LOCAL_HEATMAP_MODE) {
+ pr_err("Touch IC not in local heatmap mode: %X %X %i",
+ report.prefix, report.mode, report.counter);
+ heatmap_enable();
+ return false;
+ }
+
+ le16_to_cpus(&report.counter); /* enforce little-endian order */
+ if (report.counter == counter && counter != 0) {
+ /*
+ * We shouldn't make ordered comparisons because of
+ * potential overflow, but at least the value
+ * should have changed. If the value remains the same,
+ * but we are processing a new interrupt,
+ * this could indicate slowness in the interrupt
+ * handler.
+ */
+ if (info->mf_state != FTS_MF_UNFILTERED)
+ pr_warn("Heatmap frame has stale counter value %i",
+ counter);
+ }
+ counter = report.counter;
+ num_elements = report.size_x * report.size_y;
+ if (num_elements > LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT) {
+ pr_err("Unexpected heatmap size: %i x %i",
+ report.size_x, report.size_y);
+ return false;
+ }
+
+ /* set all to zero, will only write to non-zero locations in
+ * the loop
+ */
+ memset(v4l2->frame, 0, v4l2->format.sizeimage);
+ /* populate the data buffer, rearranging into final locations */
+ for (local_i = 0; local_i < num_elements; local_i++) {
+ /* enforce little-endian order */
+ le16_to_cpus(&report.data[local_i]);
+ heatmap_value = report.data[local_i];
+
+ if (heatmap_value == 0) {
+ /* Already set to zero. Nothing to do */
+ continue;
+ }
+
+ heatmap_x = report.offset_x + (local_i % report.size_x);
+ heatmap_y = report.offset_y + (local_i / report.size_x);
+
+ if (heatmap_x < 0 || heatmap_x >= max_x ||
+ heatmap_y < 0 || heatmap_y >= max_y) {
+ pr_err("Invalid x or y: (%i, %i), value=%i, ending loop\n",
+ heatmap_x, heatmap_y, heatmap_value);
+ return false;
+ }
+ frame_i = heatmap_y * max_x + heatmap_x;
+ v4l2->frame[frame_i] = heatmap_value;
+ }
+ } else if (info->heatmap_mode == FTS_HEATMAP_FULL) {
+ MutualSenseFrame ms_frame = { 0 };
+ uint32_t frame_index = 0, x, y;
+
+ result = getMSFrame3(MS_STRENGTH, &ms_frame);
+ if (result <= 0) {
+ pr_err("getMSFrame3 failed with result=0x%08X.\n",
+ result);
+ return false;
+ }
+
+ for (y = 0; y < max_y; y++) {
+ for (x = 0; x < max_x; x++) {
+ /* Rotate frame counter-clockwise and invert
+ * if necessary.
+ */
+ if (!info->board->sensor_inverted) {
+ heatmap_value =
+ (strength_t)ms_frame.node_data[
+ ((max_x-1) - x) * max_y + y];
+ } else {
+ heatmap_value =
+ (strength_t)ms_frame.node_data[
+ ((max_x-1) - x) * max_y +
+ ((max_y-1) - y)];
+ }
+ v4l2->frame[frame_index++] = heatmap_value;
+ }
+ }
+
+ kfree(ms_frame.node_data);
+ } else
+ return false;
+
+ return true;
+}
+#endif
+
+/* Update a state machine used to toggle control of the touch IC's motion
+ * filter.
+ */
+static int update_motion_filter(struct fts_ts_info *info)
+{
+ /* Motion filter timeout, in milliseconds */
+ const u32 mf_timeout_ms = 500;
+ u8 next_state;
+ u8 touches = hweight32(info->touch_id); /* Count the active touches */
+
+ if (info->use_default_mf)
+ return 0;
+
+ /* Determine the next filter state. The motion filter is enabled by
+ * default and it is disabled while a single finger is touching the
+ * screen. If another finger is touched down or if a timeout expires,
+ * the motion filter is reenabled and remains enabled until all fingers
+ * are lifted.
+ */
+ next_state = info->mf_state;
+ switch (info->mf_state) {
+ case FTS_MF_FILTERED:
+ if (touches == 1) {
+ next_state = FTS_MF_UNFILTERED;
+ info->mf_downtime = ktime_get();
+ }
+ break;
+ case FTS_MF_UNFILTERED:
+ if (touches == 0) {
+ next_state = FTS_MF_FILTERED;
+ } else if (touches > 1 ||
+ ktime_after(ktime_get(),
+ ktime_add_ms(info->mf_downtime,
+ mf_timeout_ms))) {
+ next_state = FTS_MF_FILTERED_LOCKED;
+ }
+ break;
+ case FTS_MF_FILTERED_LOCKED:
+ if (touches == 0) {
+ next_state = FTS_MF_FILTERED;
+ }
+ break;
+ }
+
+ /* Send command to update filter state */
+ if ((next_state == FTS_MF_UNFILTERED) !=
+ (info->mf_state == FTS_MF_UNFILTERED)) {
+ u8 cmd[3] = {0xC0, 0x05, 0x00};
+ pr_debug("%s: setting motion filter = %s.\n", __func__,
+ (next_state == FTS_MF_UNFILTERED) ? "false" : "true");
+ cmd[2] = (next_state == FTS_MF_UNFILTERED) ? 0x01 : 0x00;
+ fts_write(cmd, sizeof(cmd));
+ }
+ info->mf_state = next_state;
+
+ return 0;
+}
+
+/**
+ * Bottom Half Interrupt Handler function
+ * This handler is called each time there is at least one new event in the FIFO
+ * and the interrupt pin of the IC goes low. It will read all the events from
+ * the FIFO and dispatch them to the proper event handler according the event
+ * ID
+ */
+static irqreturn_t fts_interrupt_handler(int irq, void *handle)
+{
+ struct fts_ts_info *info = handle;
+ int error = 0, count = 0;
+ unsigned char regAdd = FIFO_CMD_READALL;
+ unsigned char data[FIFO_EVENT_SIZE * FIFO_DEPTH];
+ unsigned char eventId;
+ const unsigned char EVENTS_REMAINING_POS = 7;
+ const unsigned char EVENTS_REMAINING_MASK = 0x1F;
+ unsigned char events_remaining = 0;
+ unsigned char *evt_data;
+ bool processed_pointer_event = false;
+
+ /* It is possible that interrupts were disabled while the handler is
+ * executing, before acquiring the mutex. If so, simply return.
+ */
+ if (fts_set_bus_ref(info, FTS_BUS_REF_IRQ, true) < 0) {
+ fts_set_bus_ref(info, FTS_BUS_REF_IRQ, false);
+ return IRQ_HANDLED;
+ }
+
+ /* prevent CPU from entering deep sleep */
+ pm_qos_update_request(&info->pm_qos_req, 100);
+
+ __pm_wakeup_event(info->wakesrc, jiffies_to_msecs(HZ));
+
+ /* Read the first FIFO event and the number of events remaining */
+ error = fts_writeReadU8UX(regAdd, 0, 0, data, FIFO_EVENT_SIZE,
+ DUMMY_FIFO);
+ events_remaining = data[EVENTS_REMAINING_POS] & EVENTS_REMAINING_MASK;
+ events_remaining = (events_remaining > FIFO_DEPTH - 1) ?
+ FIFO_DEPTH - 1 : events_remaining;
+
+ /* Drain the rest of the FIFO, up to 31 events */
+ if (error == OK && events_remaining > 0) {
+ error = fts_writeReadU8UX(regAdd, 0, 0, &data[FIFO_EVENT_SIZE],
+ FIFO_EVENT_SIZE * events_remaining,
+ DUMMY_FIFO);
+ }
+ if (error != OK) {
+ pr_err("Error (%08X) while reading from FIFO in fts_event_handler\n",
+ error);
+ } else {
+ for (count = 0; count < events_remaining + 1; count++) {
+ evt_data = &data[count * FIFO_EVENT_SIZE];
+
+ if (evt_data[0] == EVT_ID_NOEVENT)
+ break;
+
+ eventId = evt_data[0] >> 4;
+
+ /* Ensure event ID is within bounds */
+ if (eventId < NUM_EVT_ID) {
+ processed_pointer_event = info->event_dispatch_table[eventId](
+ info, evt_data);
+ }
+ }
+ }
+
+ if (info->touch_id == 0)
+ input_report_key(info->input_dev, BTN_TOUCH, 0);
+
+ input_sync(info->input_dev);
+
+#ifdef TOUCHSCREEN_HEATMAP
+ if (processed_pointer_event)
+ heatmap_read(&info->v4l2, ktime_to_ns(info->timestamp));
+#endif
+
+ /* Disable the firmware motion filter during single touch */
+ update_motion_filter(info);
+
+ pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+ fts_set_bus_ref(info, FTS_BUS_REF_IRQ, false);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Read the display panel's extinfo from the display driver.
+ *
+ * The display driver finds out the extinfo is available for a panel based on
+ * the device tree, but cannot read the extinfo itself until the DSI bus is
+ * initialized. Since the extinfo is not guaranteed to be available at the time
+ * the touch driver is probed or even when the automatic firmware update work is
+ * run. The display driver's API for reading extinfo does allow a client to
+ * query the size of the expected data and whether it is available.
+ *
+ * This function retrieves the extinfo from the display driver with an optional
+ * retry period to poll the display driver before giving up.
+ *
+ * @return 0 if success, -EBUSY if timeout
+ */
+static int fts_read_panel_extinfo(struct fts_ts_info *info, int wait_seconds)
+{
+ const int RETRIES_PER_S = 4;
+ const int MS_PER_RETRY = 1000 / RETRIES_PER_S;
+ ssize_t len = -EBUSY;
+ int retries = (wait_seconds <= 0) ? 0 : wait_seconds * RETRIES_PER_S;
+ int ret = 0;
+
+ /* Was extinfo previously retrieved? */
+ if (info->extinfo.is_read)
+ return 0;
+
+ /* Extinfo should not be retrieved if the driver was unable to identify
+ * the panel via its panelmap. Consider the extinfo zero-length.
+ */
+ if (!info->board->panel) {
+ info->extinfo.is_read = true;
+ info->extinfo.size = 0;
+ return 0;
+ }
+
+ /* Obtain buffer size */
+ len = dsi_panel_read_vendor_extinfo(info->board->panel, NULL, 0);
+ if (len == 0) {
+ /* No extinfo to be consumed */
+ info->extinfo.size = 0;
+ info->extinfo.is_read = true;
+ return 0;
+ } else if (len < 0) {
+ ret = len;
+ pr_err("%s: dsi_panel_read_vendor_extinfo returned unexpected error = %d.\n",
+ __func__, ret);
+ goto error;
+ } else {
+ info->extinfo.data = kzalloc(len, GFP_KERNEL);
+ if (!info->extinfo.data) {
+ pr_err("%s: failed to allocate extinfo. len=%d.\n",
+ __func__, len);
+ ret = -ENOMEM;
+ goto error;
+ }
+ info->extinfo.size = len;
+ }
+
+ /* Read vendor extinfo data */
+ do {
+ len = dsi_panel_read_vendor_extinfo(info->board->panel,
+ info->extinfo.data,
+ info->extinfo.size);
+ if (len == -EBUSY) {
+ pr_debug("%s: sleeping %dms.\n", __func__,
+ MS_PER_RETRY);
+ msleep(MS_PER_RETRY);
+ } else if (len == info->extinfo.size) {
+ info->extinfo.is_read = true;
+ pr_debug("%s: Ultimately waited %d seconds.\n",
+ __func__,
+ wait_seconds - (retries / RETRIES_PER_S));
+ return 0;
+ } else {
+ pr_err("%s: dsi_panel_read_vendor_extinfo returned error = %d\n",
+ __func__, len);
+ ret = len;
+ goto error;
+ }
+ } while (--retries > 0);
+
+ /* Time out after retrying for wait_seconds */
+ pr_err("%s: Timed out after waiting %d seconds.\n", __func__,
+ wait_seconds);
+ ret = -EBUSY;
+
+error:
+ kfree(info->extinfo.data);
+ info->extinfo.data = NULL;
+ info->extinfo.size = 0;
+ info->extinfo.is_read = false;
+ return ret;
+}
+
+/*
+ * Determine the display panel based on the device tree and any extinfo read
+ * from the panel.
+ *
+ * Basic panel detection (e.g., unique part numbers) is performed by polling for
+ * connected drm_panels. Next, an override table from the device tree is used to
+ * parse the panel's extended info to distinguish between panel varients that
+ * require different firmware.
+ */
+static int fts_identify_panel(struct fts_ts_info *info)
+{
+ /* Formatting of EXTINFO rows provided in the device trees */
+ const int EXTINFO_ROW_ELEMS = 5;
+ const int EXTINFO_ROW_SIZE = EXTINFO_ROW_ELEMS * sizeof(u32);
+
+ struct device_node *np = info->dev->of_node;
+ u32 panel_index = info->board->initial_panel_index;
+ int extinfo_rows;
+ u32 filter_panel_index, filter_extinfo_index, filter_extinfo_mask;
+ u32 filter_extinfo_value, filter_extinfo_fw;
+ const char *name;
+ u32 inverted;
+ int i;
+ int ret = 0;
+
+ if (!info->extinfo.is_read) {
+ /* Extinfo was not read. Attempt one read before aborting */
+ ret = fts_read_panel_extinfo(info, 0);
+ if (ret < 0) {
+ pr_err("%s: fts_read_panel_extinfo failed with ret=%d.\n",
+ __func__, ret);
+ goto get_panel_info_failed;
+ }
+ }
+
+ /* Read the extinfo override table to determine if there are is any
+ * reason to select a different firmware for the panel.
+ */
+ if (of_property_read_bool(np, "st,extinfo_override_table")) {
+ extinfo_rows = of_property_count_elems_of_size(
+ np, "st,extinfo_override_table",
+ EXTINFO_ROW_SIZE);
+
+ for (i = 0; i < extinfo_rows; i++) {
+ of_property_read_u32_index(
+ np, "st,extinfo_override_table",
+ i * EXTINFO_ROW_ELEMS + 0,
+ &filter_panel_index);
+
+ of_property_read_u32_index(
+ np, "st,extinfo_override_table",
+ i * EXTINFO_ROW_ELEMS + 1,
+ &filter_extinfo_index);
+
+ of_property_read_u32_index(
+ np, "st,extinfo_override_table",
+ i * EXTINFO_ROW_ELEMS + 2,
+ &filter_extinfo_mask);
+
+ of_property_read_u32_index(
+ np, "st,extinfo_override_table",
+ i * EXTINFO_ROW_ELEMS + 3,
+ &filter_extinfo_value);
+
+ of_property_read_u32_index(
+ np, "st,extinfo_override_table",
+ i * EXTINFO_ROW_ELEMS + 4,
+ &filter_extinfo_fw);
+
+ if (panel_index != filter_panel_index)
+ continue;
+ else if (filter_extinfo_index >= info->extinfo.size) {
+ pr_err("%s: extinfo index is out of bounds (%d >= %d) in row %d of extinfo_override_table.\n",
+ __func__, filter_extinfo_index,
+ info->extinfo.size, i);
+ continue;
+ } else if ((info->extinfo.data[filter_extinfo_index] &
+ filter_extinfo_mask) ==
+ filter_extinfo_value) {
+ /* Override the panel_index as specified in the
+ * override table.
+ */
+ panel_index = filter_extinfo_fw;
+ pr_info("%s: Overriding with row=%d, panel_index=%d.\n",
+ __func__, i, panel_index);
+ break;
+ }
+ }
+ } else {
+ pr_err("%s: of_property_read_bool(np, \"st,extinfo_override_table\") failed.\n",
+ __func__);
+ }
+
+ //---------------------------------------------------------------------
+ // Read firmware name, limits file name, and sensor inversion based on
+ // the final panel index. In order to handle the case where the DRM
+ // panel was not detected from the list in the device tree, fall back to
+ // using predefined FW and limits paths hardcoded into the driver.
+ // --------------------------------------------------------------------
+get_panel_info_failed:
+ name = NULL;
+ if (info->board->panel)
+ of_property_read_string_index(np, "st,firmware_names",
+ panel_index, &name);
+ if (!name)
+ info->board->fw_name = PATH_FILE_FW;
+ else
+ info->board->fw_name = name;
+ pr_info("firmware name = %s\n", info->board->fw_name);
+
+ name = NULL;
+ if (info->board->panel)
+ of_property_read_string_index(np, "st,limits_names",
+ panel_index, &name);
+ if (!name)
+ info->board->limits_name = LIMITS_FILE;
+ else
+ info->board->limits_name = name;
+ pr_info("limits name = %s\n", info->board->limits_name);
+
+ inverted = 0;
+ if (info->board->panel)
+ of_property_read_u32_index(np, "st,sensor_inverted",
+ panel_index, &inverted);
+ info->board->sensor_inverted = (inverted != 0);
+ pr_info("Sensor inverted = %u\n", inverted);
+
+ return ret;
+}
+
+/**
+ * Implement the fw update and initialization flow of the IC that should
+ * be executed at every boot up. The function perform a fw update of the
+ * IC in case of crc error or a new fw version and then understand if the
+ * IC need to be re-initialized again.
+ *
+ * @return OK if success or an error code which specify the type of error
+ * encountered
+ */
+static int fts_fw_update(struct fts_ts_info *info)
+{
+ u8 error_to_search[4] = { EVT_TYPE_ERROR_CRC_CX_HEAD,
+ EVT_TYPE_ERROR_CRC_CX,
+ EVT_TYPE_ERROR_CRC_CX_SUB_HEAD,
+ EVT_TYPE_ERROR_CRC_CX_SUB };
+ int ret;
+ int error = 0;
+ int init_type = NO_INIT;
+ int index;
+ int prop_len = 0;
+ struct device_node *np = info->dev->of_node;
+
+#if defined(PRE_SAVED_METHOD) || defined(COMPUTE_INIT_METHOD)
+ int keep_cx = 1;
+#else
+ int keep_cx = 0;
+#endif
+
+ /* Read extinfo from display driver. Wait for up to ten seconds if
+ * there is extinfo to read but is not yet available.
+ */
+ ret = fts_read_panel_extinfo(info, 10);
+ if (ret < 0)
+ pr_err("%s: Failed or timed out during read of extinfo. ret=%d\n",
+ __func__, ret);
+
+ /* Identify panel given extinfo that may have been received. */
+ ret = fts_identify_panel(info);
+ if (ret < 0) {
+ pr_err("%s: Encountered error while identifying display panel. ret=%d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ pr_info("Fw Auto Update is starting...\n");
+
+ /* Check CRC status */
+ ret = fts_crc_check();
+ if (ret > OK) {
+ pr_err("%s: CRC Error or NO FW!\n", __func__);
+ info->reflash_fw = 1;
+ } else {
+ pr_info("%s: NO CRC Error or Impossible to read CRC register!\n",
+ __func__);
+ }
+
+ if (of_property_read_bool(np, "st,force-pi-cfg-ver-map")) {
+ prop_len = of_property_count_u32_elems(np,
+ "st,force-pi-cfg-ver-map");
+ info->board->force_pi_cfg_ver = devm_kzalloc(info->dev,
+ sizeof(u32) * prop_len, GFP_KERNEL);
+ if (info->board->force_pi_cfg_ver != NULL) {
+ for (index = 0; index < prop_len; index++) {
+ of_property_read_u32_index(np,
+ "st,force-pi-cfg-ver-map",
+ index,
+ &info->board->force_pi_cfg_ver[index]);
+ pr_info("%s: force PI config version: %04X",
+ __func__,
+ info->board->force_pi_cfg_ver[index]);
+ if(systemInfo.u16_cfgVer ==
+ info->board->force_pi_cfg_ver[index]) {
+ pr_info("%s System config version %04X, do panel init",
+ __func__, systemInfo.u16_cfgVer);
+ init_type = SPECIAL_PANEL_INIT;
+ }
+ }
+ } else {
+ pr_err("%s: force_pi_cfg_ver is NULL", __func__);
+ }
+ } else {
+ pr_info("%s: of_property_read_bool(np, \"st,force-pi-cfg-ver-map\") failed.\n",
+ __func__);
+ }
+
+ if (info->board->auto_fw_update) {
+ ret = flashProcedure(info->board->fw_name, info->reflash_fw,
+ keep_cx);
+ if ((ret & 0xF000000F) == ERROR_FILE_NOT_FOUND) {
+ pr_err("%s: firmware file not found. Bypassing update.\n",
+ __func__);
+ ret = 0;
+ goto out;
+ } else if ((ret & 0xFF000000) == ERROR_FLASH_PROCEDURE) {
+ pr_err("%s: firmware update failed; retrying. ERROR %08X\n",
+ __func__, ret);
+ /* Power cycle the touch IC */
+ fts_chip_powercycle(info);
+ ret = flashProcedure(info->board->fw_name,
+ info->reflash_fw, keep_cx);
+ if ((ret & 0xFF000000) == ERROR_FLASH_PROCEDURE) {
+ pr_err("%s: firmware update failed again! ERROR %08X\n",
+ __func__, ret);
+ pr_err("Fw Auto Update Failed!\n");
+ return ret;
+ }
+ }
+ info->reflash_fw = 0;
+ }
+
+ pr_info("%s: Verifying if CX CRC Error...\n", __func__);
+ ret = fts_system_reset();
+ if (ret >= OK) {
+ ret = pollForErrorType(error_to_search, 4);
+ if (ret < OK) {
+ pr_info("%s: No Cx CRC Error Found!\n", __func__);
+ pr_info("%s: Verifying if Panel CRC Error...\n",
+ __func__);
+ error_to_search[0] = EVT_TYPE_ERROR_CRC_PANEL_HEAD;
+ error_to_search[1] = EVT_TYPE_ERROR_CRC_PANEL;
+ ret = pollForErrorType(error_to_search, 2);
+ if (ret < OK) {
+ pr_info("%s: No Panel CRC Error Found!\n",
+ __func__);
+ } else {
+ pr_err("%s: Panel CRC Error FOUND! CRC ERROR = %02X\n",
+ __func__, ret);
+ init_type = SPECIAL_PANEL_INIT;
+ }
+ } else {
+ pr_err("%s: Cx CRC Error FOUND! CRC ERROR = %02X\n",
+ __func__, ret);
+
+ /** This path of the code is used only in case there is
+ * a CRC error in code or config which not allow the fw
+ * to compute the CRC in the CX before
+ */
+#ifndef COMPUTE_INIT_METHOD
+ pr_info("%s: Try to recovery with CX in fw file...\n",
+ __func__);
+ ret = flashProcedure(info->board->fw_name, CRC_CX, 0);
+ pr_info("%s: Refresh panel init data", __func__);
+#else
+ pr_info("%s: Select Full Panel Init...\n", __func__);
+ init_type = SPECIAL_FULL_PANEL_INIT;
+#endif
+ }
+ } else {
+ /* Skip initialization because the real state is unknown */
+ pr_err("%s: Error while executing system reset! ERROR %08X\n",
+ __func__, ret);
+ }
+
+ if (init_type != SPECIAL_FULL_PANEL_INIT) {
+#if defined(PRE_SAVED_METHOD) || defined(COMPUTE_INIT_METHOD)
+ if ((systemInfo.u8_cfgAfeVer != systemInfo.u8_cxAfeVer)
+#ifdef COMPUTE_INIT_METHOD
+ || ((systemInfo.u8_mpFlag != MP_FLAG_BOOT) &&
+ (systemInfo.u8_mpFlag != MP_FLAG_FACTORY))
+#endif
+ ) {
+ init_type = SPECIAL_FULL_PANEL_INIT;
+ pr_err("%s: Different CX AFE Ver: %02X != %02X or invalid MpFlag = %02X... Execute FULL Panel Init!\n",
+ __func__, systemInfo.u8_cfgAfeVer,
+ systemInfo.u8_cxAfeVer, systemInfo.u8_mpFlag);
+ } else
+#endif
+ if (systemInfo.u8_cfgAfeVer != systemInfo.u8_panelCfgAfeVer) {
+ init_type = SPECIAL_PANEL_INIT;
+ pr_err("%s: Different Panel AFE Ver: %02X != %02X... Execute Panel Init!\n",
+ __func__, systemInfo.u8_cfgAfeVer,
+ systemInfo.u8_panelCfgAfeVer);
+ }
+ }
+
+out:
+
+ if (init_type != NO_INIT) { /* initialization status not correct or
+ * after FW complete update, do
+ * initialization.
+ */
+ error = fts_chip_initialization(info, init_type);
+ if (error < OK) {
+ pr_err("%s: Cannot initialize the chip ERROR %08X\n",
+ __func__, error);
+ }
+
+ /* Reset after initialization */
+ ret = fts_system_reset();
+ if (ret < OK) {
+ pr_err("%s: Reset failed, ERROR %08X\n", __func__,
+ ret);
+ }
+ }
+
+ error = fts_init_sensing(info);
+ if (error < OK) {
+ pr_err("Cannot initialize the hardware device ERROR %08X\n",
+ error);
+ }
+
+ pr_err("Fw Update Finished! error = %08X\n", error);
+ return error;
+}
+
+/**
+ * Function called by the delayed workthread executed after the probe in
+ * order to perform the fw update flow
+ * @see fts_fw_update()
+ */
+static void fts_fw_update_auto(struct work_struct *work)
+{
+ struct delayed_work *fwu_work = container_of(work, struct delayed_work,
+ work);
+ struct fts_ts_info *info = container_of(fwu_work, struct fts_ts_info,
+ fwu_work);
+
+ fts_set_bus_ref(info, FTS_BUS_REF_FW_UPDATE, true);
+ fts_fw_update(info);
+ fts_set_bus_ref(info, FTS_BUS_REF_FW_UPDATE, false);
+}
+
+/* TODO: define if need to do the full mp at the boot */
+/**
+ * Execute the initialization of the IC (supporting a retry mechanism),
+ * checking also the resulting data
+ * @see production_test_main()
+ */
+static int fts_chip_initialization(struct fts_ts_info *info, int init_type)
+{
+ int ret2 = 0;
+ int retry;
+ int initretrycnt = 0;
+#ifdef COMPUTE_INIT_METHOD
+ const char *limits_file = info->board->limits_name;
+#endif
+
+ /* initialization error, retry initialization */
+ for (retry = 0; retry < RETRY_INIT_BOOT; retry++) {
+#ifndef COMPUTE_INIT_METHOD
+ ret2 = production_test_initialization(init_type);
+#else
+ ret2 = production_test_main(limits_file, 1, init_type, &tests,
+ MP_FLAG_BOOT);
+#endif
+ if (ret2 == OK)
+ break;
+ initretrycnt++;
+ pr_err("initialization cycle count = %04d - ERROR %08X\n",
+ initretrycnt, ret2);
+ fts_chip_powercycle(info);
+ }
+
+ if (ret2 < OK) /* initialization error */
+ pr_err("fts initialization failed %d times\n", RETRY_INIT_BOOT);
+
+ return ret2;
+}
+
+
+static irqreturn_t fts_isr(int irq, void *handle)
+{
+ struct fts_ts_info *info = handle;
+
+ info->timestamp = ktime_get();
+ input_set_timestamp(info->input_dev, info->timestamp);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * Initialize the dispatch table with the event handlers for any possible event
+ * ID
+ * Set IRQ pin behavior (level triggered low)
+ * Register top half interrupt handler function.
+ * @see fts_interrupt_handler()
+ */
+static int fts_interrupt_install(struct fts_ts_info *info)
+{
+ int i, error = 0;
+
+ info->event_dispatch_table = kzalloc(sizeof(event_dispatch_handler_t) *
+ NUM_EVT_ID, GFP_KERNEL);
+
+ if (!info->event_dispatch_table) {
+ pr_err("OOM allocating event dispatch table\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < NUM_EVT_ID; i++)
+ info->event_dispatch_table[i] = fts_nop_event_handler;
+
+ install_handler(info, ENTER_POINT, enter_pointer);
+ install_handler(info, LEAVE_POINT, leave_pointer);
+ install_handler(info, MOTION_POINT, motion_pointer);
+ install_handler(info, ERROR, error);
+ install_handler(info, CONTROLLER_READY, controller_ready);
+ install_handler(info, STATUS_UPDATE, status);
+ install_handler(info, USER_REPORT, user_report);
+
+ /* disable interrupts in any case */
+ error = fts_enableInterrupt(false);
+ if (error) {
+ return error;
+ }
+
+ error = request_threaded_irq(info->client->irq, fts_isr,
+ fts_interrupt_handler, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ FTS_TS_DRV_NAME, info);
+ info->irq_enabled = true;
+
+ if (error) {
+ pr_err("Request irq failed\n");
+ kfree(info->event_dispatch_table);
+ }
+
+ return error;
+}
+
+/**
+ * Clean the dispatch table and the free the IRQ.
+ * This function is called when the driver need to be removed
+ */
+static void fts_interrupt_uninstall(struct fts_ts_info *info)
+{
+ fts_enableInterrupt(false);
+
+ kfree(info->event_dispatch_table);
+
+ free_irq(info->client->irq, info);
+}
+
+/**@}*/
+
+/**
+ * This function try to attempt to communicate with the IC for the first time
+ * during the boot up process in order to read the necessary info for the
+ * following stages.
+ * The function execute a system reset, read fundamental info (system info)
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_init(struct fts_ts_info *info)
+{
+ int error;
+
+
+ error = fts_system_reset();
+ if (error < OK && isI2cError(error)) {
+ pr_err("Cannot reset the device! ERROR %08X\n", error);
+ return error;
+ } else {
+ if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) {
+ pr_err("Setting default Sys INFO!\n");
+ error = defaultSysInfo(0);
+ } else {
+ error = readSysInfo(0); /* system reset OK */
+ if (error < OK) {
+ if (!isI2cError(error))
+ error = OK;
+ pr_err("Cannot read Sys Info! ERROR %08X\n",
+ error);
+ }
+ }
+ }
+
+ return error;
+}
+
+/**
+ * Execute a power cycle in the IC, toggling the power lines (AVDD and DVDD)
+ * @param info pointer to fts_ts_info struct which contain information of the
+ * regulators
+ * @return 0 if success or another value if fail
+ */
+int fts_chip_powercycle(struct fts_ts_info *info)
+{
+ int error = 0;
+
+ pr_info("%s: Power Cycle Starting...\n", __func__);
+ pr_info("%s: Disabling IRQ...\n", __func__);
+ /** if IRQ pin is short with DVDD a call to the ISR will triggered when
+ * the regulator is turned off if IRQ not disabled */
+ fts_enableInterrupt(false);
+
+ if (info->vdd_reg) {
+ error = regulator_disable(info->vdd_reg);
+ if (error < 0)
+ pr_err("%s: Failed to disable DVDD regulator\n",
+ __func__);
+ }
+
+ if (info->avdd_reg) {
+ error = regulator_disable(info->avdd_reg);
+ if (error < 0)
+ pr_err("%s: Failed to disable AVDD regulator\n",
+ __func__);
+ }
+
+ if (info->board->reset_gpio != GPIO_NOT_DEFINED)
+ gpio_set_value(info->board->reset_gpio, 0);
+ else
+ mdelay(300);
+
+ /* in FTI power up first the digital and then the analog */
+ if (info->vdd_reg) {
+ error = regulator_enable(info->vdd_reg);
+ if (error < 0)
+ pr_err("%s: Failed to enable DVDD regulator\n",
+ __func__);
+ }
+
+ mdelay(1);
+
+ if (info->avdd_reg) {
+ error = regulator_enable(info->avdd_reg);
+ if (error < 0)
+ pr_err("%s: Failed to enable AVDD regulator\n",
+ __func__);
+ }
+
+ mdelay(5); /* time needed by the regulators for reaching the regime
+ * values */
+
+
+ if (info->board->reset_gpio != GPIO_NOT_DEFINED) {
+ mdelay(10); /* time to wait before bring up the reset
+ * gpio after the power up of the regulators */
+ gpio_set_value(info->board->reset_gpio, 1);
+ }
+
+ release_all_touches(info);
+
+ pr_info("%s: Power Cycle Finished! ERROR CODE = %08x\n",
+ __func__, error);
+ setSystemResetedUp(1);
+ setSystemResetedDown(1);
+ return error;
+}
+
+
+/**
+ * Complete the boot up process, initializing the sensing of the IC according
+ * to the current setting chosen by the host
+ * Register the notifier for the suspend/resume actions and the event handler
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_init_sensing(struct fts_ts_info *info)
+{
+ int error = 0;
+
+#ifdef CONFIG_DRM
+ error |= drm_panel_notifier_register(info->board->panel,
+ &info->notifier); /* register the
+ * suspend/resume
+ * function */
+#endif
+ error |= fts_interrupt_install(info); /* register event handler */
+ error |= fts_mode_handler(info, 0); /* enable the features and
+ * sensing */
+ error |= fts_enableInterrupt(true); /* enable the interrupt */
+
+ if (error < OK)
+ pr_err("%s Init after Probe error (ERROR = %08X)\n",
+ __func__, error);
+
+#ifdef TOUCHSCREEN_HEATMAP
+ heatmap_enable();
+#endif
+
+ return error;
+}
+
+/* TODO: change this function according with the needs of customer in terms
+ * of feature to enable/disable */
+
+/**
+ * @ingroup mode_section
+ * @{
+ */
+/**
+ * The function handle the switching of the mode in the IC enabling/disabling
+ * the sensing and the features set from the host
+ * @param info pointer to fts_ts_info which contains info about the device and
+ * its hw setup
+ * @param force if 1, the enabling/disabling command will be send even
+ * if the feature was already enabled/disabled otherwise it will judge if
+ * the feature changed status or the IC had a system reset
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_mode_handler(struct fts_ts_info *info, int force)
+{
+ int res = OK;
+ int ret = OK;
+ u8 settings[4] = { 0 };
+
+ /* disable irq wake because resuming from gesture mode */
+ if (IS_POWER_MODE(info->mode, SCAN_MODE_LOW_POWER) &&
+ (info->resume_bit == 1))
+ disable_irq_wake(info->client->irq);
+
+ info->mode = MODE_NOTHING; /* initialize the mode to nothing
+ * in order to be updated depending
+ * on the features enabled */
+
+ pr_debug("%s: Mode Handler starting...\n", __func__);
+ switch (info->resume_bit) {
+ case 0: /* screen down */
+ pr_debug("%s: Screen OFF...\n", __func__);
+ /* do sense off in order to avoid the flooding of the fifo with
+ * touch events if someone is touching the panel during suspend
+ **/
+ pr_info("%s: Sense OFF!\n", __func__);
+ /* for speed reason (no need to check echo in this case and
+ * interrupt can be enabled) */
+ ret = setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ res |= ret; /* to avoid warning unsused ret variable when a
+ * ll the features are disabled */
+
+#ifdef GESTURE_MODE
+ if (info->gesture_enabled == 1) {
+ pr_info("%s: enter in gesture mode !\n",
+ __func__);
+ res = enterGestureMode(isSystemResettedDown());
+ if (res >= OK) {
+ enable_irq_wake(info->client->irq);
+ fromIDtoMask(FEAT_SEL_GESTURE,
+ (u8 *)&info->mode,
+ sizeof(info->mode));
+ MODE_LOW_POWER(info->mode, 0);
+ } else
+ pr_err("%s: enterGestureMode failed! ERROR %08X recovery in senseOff...\n",
+ __func__, res);
+ }
+#endif
+
+ setSystemResetedDown(0);
+ break;
+
+ case 1: /* screen up */
+ pr_debug("%s: Screen ON...\n", __func__);
+
+#ifdef GLOVE_MODE
+ if ((info->glove_enabled == FEAT_ENABLE &&
+ isSystemResettedUp()) || force == 1) {
+ pr_info("%s: Glove Mode setting...\n", __func__);
+ settings[0] = info->glove_enabled;
+ /* required to satisfy also the disable case */
+ ret = setFeatures(FEAT_SEL_GLOVE, settings, 1);
+ if (ret < OK)
+ pr_err("%s: error during setting GLOVE_MODE! ERROR %08X\n",
+ __func__, ret);
+ res |= ret;
+
+ if (ret >= OK && info->glove_enabled == FEAT_ENABLE) {
+ fromIDtoMask(FEAT_SEL_GLOVE, (u8 *)&info->mode,
+ sizeof(info->mode));
+ pr_info("%s: GLOVE_MODE Enabled!\n", __func__);
+ } else
+ pr_info("%s: GLOVE_MODE Disabled!\n", __func__);
+ }
+
+#endif
+
+#ifdef COVER_MODE
+ if ((info->cover_enabled == FEAT_ENABLE &&
+ isSystemResettedUp()) || force == 1) {
+ pr_info("%s: Cover Mode setting...\n", __func__);
+ settings[0] = info->cover_enabled;
+ ret = setFeatures(FEAT_SEL_COVER, settings, 1);
+ if (ret < OK)
+ pr_err("%s: error during setting COVER_MODE! ERROR %08X\n",
+ __func__, ret);
+ res |= ret;
+
+ if (ret >= OK && info->cover_enabled == FEAT_ENABLE) {
+ fromIDtoMask(FEAT_SEL_COVER, (u8 *)&info->mode,
+ sizeof(info->mode));
+ pr_info("%s: COVER_MODE Enabled!\n", __func__);
+ } else
+ pr_info("%s: COVER_MODE Disabled!\n", __func__);
+ }
+#endif
+#ifdef CHARGER_MODE
+ if ((info->charger_enabled > 0 && isSystemResettedUp()) ||
+ force == 1) {
+ pr_info("%s: Charger Mode setting...\n", __func__);
+
+ settings[0] = info->charger_enabled;
+ ret = setFeatures(FEAT_SEL_CHARGER, settings, 1);
+ if (ret < OK)
+ pr_err("%s: error during setting CHARGER_MODE! ERROR %08X\n",
+ __func__, ret);
+ res |= ret;
+
+ if (ret >= OK && info->charger_enabled == FEAT_ENABLE) {
+ fromIDtoMask(FEAT_SEL_CHARGER,
+ (u8 *)&info->mode,
+ sizeof(info->mode));
+ pr_info("%s: CHARGER_MODE Enabled!\n",
+ __func__);
+ } else
+ pr_info("%s: CHARGER_MODE Disabled!\n",
+ __func__);
+ }
+#endif
+
+
+#ifdef GRIP_MODE
+ if ((info->grip_enabled == FEAT_ENABLE &&
+ isSystemResettedUp()) || force == 1) {
+ pr_info("%s: Grip Mode setting...\n", __func__);
+ settings[0] = info->grip_enabled;
+ ret = setFeatures(FEAT_SEL_GRIP, settings, 1);
+ if (ret < OK)
+ pr_err("%s: error during setting GRIP_MODE! ERROR %08X\n",
+ __func__, ret);
+ res |= ret;
+
+ if (ret >= OK && info->grip_enabled == FEAT_ENABLE) {
+ fromIDtoMask(FEAT_SEL_GRIP, (u8 *)&info->mode,
+ sizeof(info->mode));
+ pr_info("%s: GRIP_MODE Enabled!\n", __func__);
+ } else
+ pr_info("%s: GRIP_MODE Disabled!\n", __func__);
+ }
+#endif
+ /* If some selective scan want to be enabled can be done
+ * an or of the following options
+ */
+ /* settings[0] = ACTIVE_MULTI_TOUCH | ACTIVE_KEY | */
+ /* ACTIVE_HOVER | ACTIVE_PROXIMITY | */
+ /* ACTIVE_FORCE; */
+ settings[0] = 0xFF; /* enable all the possible scans mode
+ * supported by the config */
+ pr_info("%s: Sense ON!\n", __func__);
+ res |= setScanMode(SCAN_MODE_ACTIVE, settings[0]);
+ info->mode |= (SCAN_MODE_ACTIVE << 24);
+ MODE_ACTIVE(info->mode, settings[0]);
+
+
+ setSystemResetedUp(0);
+ break;
+
+ default:
+ pr_err("%s: invalid resume_bit value = %d! ERROR %08X\n",
+ __func__, info->resume_bit, ERROR_OP_NOT_ALLOW);
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+
+ pr_debug("%s: Mode Handler finished! res = %08X mode = %08X\n",
+ __func__, res, info->mode);
+ return res;
+}
+
+/**
+ * Configure the switch GPIO to toggle bus master between AP and SLPI.
+ * gpio_value takes one of
+ * { FTS_SWITCH_GPIO_VALUE_SLPI_MASTER, FTS_SWITCH_GPIO_VALUE_AP_MASTER }
+ */
+static void fts_set_switch_gpio(struct fts_ts_info *info, int gpio_value)
+{
+ int retval;
+ unsigned int gpio = info->board->switch_gpio;
+
+ if (!gpio_is_valid(gpio))
+ return;
+
+ pr_debug("%s: toggling i2c switch to %s\n", __func__,
+ gpio_value == FTS_SWITCH_GPIO_VALUE_AP_MASTER ? "AP" : "SLPI");
+
+ retval = gpio_direction_output(gpio, gpio_value);
+ if (retval < 0)
+ pr_err("%s: Failed to toggle switch_gpio, err = %d\n",
+ __func__, retval);
+}
+
+/**
+ * Resume work function which perform a system reset, clean all the touches
+ * from the linux input system and prepare the ground for enabling the sensing
+ */
+static void fts_resume_work(struct work_struct *work)
+{
+ struct fts_ts_info *info;
+
+ info = container_of(work, struct fts_ts_info, resume_work);
+
+ if (!info->sensor_sleep)
+ return;
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (info->tbn)
+ tbn_request_bus(info->tbn);
+#endif
+
+ fts_set_switch_gpio(info, FTS_SWITCH_GPIO_VALUE_AP_MASTER);
+
+ fts_pinctrl_setup(info, true);
+
+ __pm_wakeup_event(info->wakesrc, jiffies_to_msecs(HZ));
+
+ info->resume_bit = 1;
+
+ fts_system_reset();
+
+ release_all_touches(info);
+
+ fts_mode_handler(info, 0);
+
+ info->sensor_sleep = false;
+
+#ifdef TOUCHSCREEN_HEATMAP
+ /* heatmap must be enabled after every chip reset (fts_system_reset) */
+ heatmap_enable();
+#endif
+
+ fts_enableInterrupt(true);
+
+ complete_all(&info->bus_resumed);
+}
+
+/**
+ * Suspend work function which clean all the touches from Linux input system
+ * and prepare the ground to disabling the sensing or enter in gesture mode
+ */
+static void fts_suspend_work(struct work_struct *work)
+{
+ struct fts_ts_info *info;
+
+ info = container_of(work, struct fts_ts_info, suspend_work);
+
+ if (info->sensor_sleep)
+ return;
+
+ reinit_completion(&info->bus_resumed);
+
+ __pm_stay_awake(info->wakesrc);
+
+ info->resume_bit = 0;
+
+ fts_mode_handler(info, 0);
+
+ release_all_touches(info);
+
+ fts_enableInterrupt(false);
+
+ /* Flush any outstanding touch events */
+ fts_system_reset();
+ flushFIFO();
+
+ fts_pinctrl_setup(info, false);
+
+ fts_set_switch_gpio(info, FTS_SWITCH_GPIO_VALUE_SLPI_MASTER);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ if (info->tbn)
+ tbn_release_bus(info->tbn);
+#endif
+
+ info->sensor_sleep = true;
+ __pm_relax(info->wakesrc);
+}
+/** @}*/
+
+
+static void fts_aggregate_bus_state(struct fts_ts_info *info)
+{
+ pr_debug("%s: bus_refmask = 0x%02X.\n", __func__,
+ info->bus_refmask);
+
+ /* Complete or cancel any outstanding transitions */
+ cancel_work_sync(&info->suspend_work);
+ cancel_work_sync(&info->resume_work);
+
+ if ((info->bus_refmask == 0 && info->sensor_sleep) ||
+ (info->bus_refmask != 0 && !info->sensor_sleep))
+ return;
+
+ if (info->bus_refmask == 0)
+ queue_work(info->event_wq, &info->suspend_work);
+ else
+ queue_work(info->event_wq, &info->resume_work);
+}
+
+int fts_set_bus_ref(struct fts_ts_info *info, u16 ref, bool enable)
+{
+ int result = OK;
+
+ mutex_lock(&info->bus_mutex);
+
+ if ((enable && (info->bus_refmask & ref)) ||
+ (!enable && !(info->bus_refmask & ref))) {
+ pr_debug("%s: reference is unexpectedly set: mask=0x%04X, ref=0x%04X, enable=%d.\n",
+ __func__, info->bus_refmask, ref, enable);
+ mutex_unlock(&info->bus_mutex);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ if (enable) {
+ /* IRQs can only keep the bus active. IRQs received while the
+ * bus is transferred to SLPI should be ignored.
+ */
+ if (ref == FTS_BUS_REF_IRQ && info->bus_refmask == 0)
+ result = ERROR_OP_NOT_ALLOW;
+ else
+ info->bus_refmask |= ref;
+ } else
+ info->bus_refmask &= ~ref;
+ fts_aggregate_bus_state(info);
+
+ mutex_unlock(&info->bus_mutex);
+
+ /* When triggering a wake, wait up to one second to resume. SCREEN_ON
+ * and IRQ references do not need to wait.
+ */
+ if (enable && ref != FTS_BUS_REF_SCREEN_ON && ref != FTS_BUS_REF_IRQ) {
+ wait_for_completion_timeout(&info->bus_resumed, HZ);
+ if (info->sensor_sleep) {
+ pr_err("%s: Failed to wake the touch bus: mask=0x%04X, ref=0x%04X, enable=%d.\n",
+ __func__, info->bus_refmask, ref, enable);
+ result = ERROR_TIMEOUT;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Callback function used to detect the suspend/resume events generated by
+ * clicking the power button.
+ * This function schedule a suspend or resume work according to the event
+ * received.
+ */
+#ifdef CONFIG_DRM
+static int fts_screen_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 (val != DRM_PANEL_EVENT_BLANK && val != DRM_PANEL_EARLY_EVENT_BLANK)
+ return NOTIFY_DONE;
+
+ if (!info || !evdata || !evdata->data) {
+ pr_info("%s: Bad fts notifier call!\n", __func__);
+ return NOTIFY_DONE;
+ }
+
+ pr_debug("%s: fts notifier begin!\n", __func__);
+
+ blank = *(int *) (evdata->data);
+ switch (blank) {
+ case DRM_PANEL_BLANK_POWERDOWN:
+ case DRM_PANEL_BLANK_LP:
+ if (val == DRM_PANEL_EARLY_EVENT_BLANK) {
+ pr_debug("%s: BLANK\n", __func__);
+#ifdef SUPPORT_PROX_PALM
+ if (info->audio_status)
+ enable_prox_palm_only_mode(true);
+#endif
+ fts_set_bus_ref(info, FTS_BUS_REF_SCREEN_ON, false);
+#ifdef SUPPORT_PROX_PALM
+ release_all_touches(info);
+#endif
+ }
+ break;
+ case DRM_PANEL_BLANK_UNBLANK:
+ if (val == DRM_PANEL_EVENT_BLANK) {
+ pr_debug("%s: UNBLANK\n", __func__);
+#ifdef SUPPORT_PROX_PALM
+ if (info->audio_status)
+ enable_prox_palm_only_mode(false);
+#endif
+ fts_set_bus_ref(info, FTS_BUS_REF_SCREEN_ON, true);
+#ifdef SUPPORT_PROX_PALM
+ release_all_touches(info);
+#endif
+ }
+ break;
+ }
+
+#ifdef DYNAMIC_REFRESH_RATE
+ if (info->display_refresh_rate != evdata->refresh_rate) {
+ info->display_refresh_rate = evdata->refresh_rate;
+ if (gpio_is_valid(info->board->disp_rate_gpio))
+ gpio_set_value(info->board->disp_rate_gpio,
+ (info->display_refresh_rate == 90));
+ pr_debug("Refresh rate changed to %d Hz.\n",
+ info->display_refresh_rate);
+ }
+#endif
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block fts_noti_block = {
+ .notifier_call = fts_screen_state_chg_callback,
+};
+#endif
+
+/**
+ * From the name of the power regulator get/put the actual regulator structs
+ * (copying their references into fts_ts_info variable)
+ * @param info pointer to fts_ts_info which contains info about the device and
+ * its hw setup
+ * @param get if 1, the regulators are get otherwise they are put (released)
+ * back to the system
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_get_reg(struct fts_ts_info *info, bool get)
+{
+ int retval;
+ const struct fts_hw_platform_data *bdata = info->board;
+
+ if (!get) {
+ retval = 0;
+ goto regulator_put;
+ }
+
+ if ((bdata->vdd_reg_name != NULL) && (*bdata->vdd_reg_name != 0)) {
+ info->vdd_reg = regulator_get(info->dev, bdata->vdd_reg_name);
+ if (IS_ERR(info->vdd_reg)) {
+ pr_err("%s: Failed to get power regulator\n", __func__);
+ retval = PTR_ERR(info->vdd_reg);
+ goto regulator_put;
+ }
+ }
+
+ if ((bdata->avdd_reg_name != NULL) && (*bdata->avdd_reg_name != 0)) {
+ info->avdd_reg = regulator_get(info->dev, bdata->avdd_reg_name);
+ if (IS_ERR(info->avdd_reg)) {
+ pr_err("%s: Failed to get bus pullup regulator\n",
+ __func__);
+ retval = PTR_ERR(info->avdd_reg);
+ goto regulator_put;
+ }
+ }
+
+ return OK;
+
+regulator_put:
+ if (info->vdd_reg) {
+ regulator_put(info->vdd_reg);
+ info->vdd_reg = NULL;
+ }
+
+ if (info->avdd_reg) {
+ regulator_put(info->avdd_reg);
+ info->avdd_reg = NULL;
+ }
+
+ return retval;
+}
+
+
+/**
+ * Enable or disable the power regulators
+ * @param info pointer to fts_ts_info which contains info about the device and
+ * its hw setup
+ * @param enable if 1, the power regulators are turned on otherwise they are
+ * turned off
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_enable_reg(struct fts_ts_info *info, bool enable)
+{
+ int retval;
+
+ if (!enable) {
+ retval = 0;
+ goto disable_pwr_reg;
+ }
+
+ if (info->vdd_reg) {
+ retval = regulator_enable(info->vdd_reg);
+ if (retval < 0) {
+ pr_err("%s: Failed to enable bus regulator\n",
+ __func__);
+ goto exit;
+ }
+ }
+
+ if (info->avdd_reg) {
+ retval = regulator_enable(info->avdd_reg);
+ if (retval < 0) {
+ pr_err("%s: Failed to enable power regulator\n",
+ __func__);
+ goto disable_bus_reg;
+ }
+ }
+
+ return OK;
+
+disable_pwr_reg:
+ if (info->avdd_reg)
+ regulator_disable(info->avdd_reg);
+
+disable_bus_reg:
+ if (info->vdd_reg)
+ regulator_disable(info->vdd_reg);
+
+exit:
+ return retval;
+}
+
+/**
+ * Configure a GPIO according to the parameters
+ * @param gpio gpio number
+ * @param config if true, the gpio is set up otherwise it is free
+ * @param dir direction of the gpio, 0 = in, 1 = out
+ * @param state initial value (if the direction is in, this parameter is
+ * ignored)
+ * return error code
+ */
+static int fts_gpio_setup(int gpio, bool config, int dir, int state)
+{
+ int retval = 0;
+ unsigned char buf[16];
+
+ if (config) {
+ scnprintf(buf, sizeof(buf), "fts_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;
+}
+
+/**
+ * Setup the IRQ and RESET (if present) gpios.
+ * If the Reset Gpio is present it will perform a cycle HIGH-LOW-HIGH in order
+ * to assure that the IC has been reset properly
+ */
+static int fts_set_gpio(struct fts_ts_info *info)
+{
+ int retval;
+ struct fts_hw_platform_data *bdata =
+ info->board;
+
+ retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0);
+ if (retval < 0) {
+ pr_err("%s: Failed to configure irq GPIO\n", __func__);
+ goto err_gpio_irq;
+ }
+
+ if (gpio_is_valid(bdata->switch_gpio)) {
+ retval = fts_gpio_setup(bdata->switch_gpio, true, 1,
+ FTS_SWITCH_GPIO_VALUE_AP_MASTER);
+ if (retval < 0)
+ pr_err("%s: Failed to configure I2C switch\n",
+ __func__);
+ }
+
+#ifdef DYNAMIC_REFRESH_RATE
+ if (gpio_is_valid(bdata->disp_rate_gpio)) {
+ retval = fts_gpio_setup(bdata->disp_rate_gpio, true, 1,
+ (info->display_refresh_rate == 90));
+ if (retval < 0)
+ pr_err("%s: Failed to configure disp_rate_gpio\n",
+ __func__);
+ }
+#endif
+
+ if (bdata->reset_gpio >= 0) {
+ retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0);
+ if (retval < 0) {
+ pr_err("%s: Failed to configure reset GPIO\n",
+ __func__);
+ goto err_gpio_reset;
+ }
+ }
+ if (bdata->reset_gpio >= 0) {
+ gpio_set_value(bdata->reset_gpio, 0);
+ mdelay(10);
+ gpio_set_value(bdata->reset_gpio, 1);
+ }
+
+ return OK;
+
+err_gpio_reset:
+ fts_gpio_setup(bdata->irq_gpio, false, 0, 0);
+ bdata->reset_gpio = GPIO_NOT_DEFINED;
+err_gpio_irq:
+ return retval;
+}
+
+/** Set pin state to active or suspend
+ * @param active 1 for active while 0 for suspend
+ */
+static void fts_pinctrl_setup(struct fts_ts_info *info, bool active)
+{
+ int retval;
+
+ if (info->ts_pinctrl) {
+ /*
+ * Pinctrl setup is optional.
+ * If pinctrl is found, set pins to active/suspend state.
+ * Otherwise, go on without showing error messages.
+ */
+ retval = pinctrl_select_state(info->ts_pinctrl, active ?
+ info->pinctrl_state_active :
+ info->pinctrl_state_suspend);
+ if (retval < 0) {
+ pr_err("Failed to select %s pinstate %d\n", active ?
+ PINCTRL_STATE_ACTIVE : PINCTRL_STATE_SUSPEND,
+ retval);
+ }
+ } else {
+ pr_warn("ts_pinctrl is NULL\n");
+ }
+}
+
+/**
+ * Get/put the touch pinctrl from the specific names. If pinctrl is used, the
+ * active and suspend pin control names and states are necessary.
+ * @param info pointer to fts_ts_info which contains info about the device and
+ * its hw setup
+ * @param get if 1, the pinctrl is get otherwise it is put (released) back to
+ * the system
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_pinctrl_get(struct fts_ts_info *info, bool get)
+{
+ int retval;
+
+ if (!get) {
+ retval = 0;
+ goto pinctrl_put;
+ }
+
+ info->ts_pinctrl = devm_pinctrl_get(info->dev);
+ if (IS_ERR_OR_NULL(info->ts_pinctrl)) {
+ retval = PTR_ERR(info->ts_pinctrl);
+ pr_info("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);
+ pr_err("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);
+ pr_err("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);
+ pr_warn("Can not lookup %s pinstate %d\n",
+ PINCTRL_STATE_RELEASE, retval);
+ }
+
+ return OK;
+
+err_pinctrl_lookup:
+ devm_pinctrl_put(info->ts_pinctrl);
+err_pinctrl_get:
+ info->ts_pinctrl = NULL;
+pinctrl_put:
+ 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))
+ pr_warn("Failed to select release pinstate\n");
+ }
+ }
+ return retval;
+}
+
+/**
+ * Retrieve and parse the hw information from the device tree node defined in
+ * the system.
+ * the most important information to obtain are: IRQ and RESET gpio numbers,
+ * power regulator names
+ * In the device file node is possible to define additional optional
+ * information
+ * that can be parsed here.
+ */
+static int parse_dt(struct device *dev, struct fts_hw_platform_data *bdata)
+{
+ int retval;
+ int index;
+ struct of_phandle_args panelmap;
+ struct drm_panel *panel = NULL;
+ struct display_timing timing;
+ const char *name;
+ struct device_node *np = dev->of_node;
+ u32 coords[2];
+
+ if (of_property_read_bool(np, "st,panel_map")) {
+ for (index = 0 ;; index++) {
+ retval = of_parse_phandle_with_fixed_args(np,
+ "st,panel_map",
+ 1,
+ index,
+ &panelmap);
+ if (retval)
+ return -EPROBE_DEFER;
+ panel = of_drm_find_panel(panelmap.np);
+ of_node_put(panelmap.np);
+ if (!IS_ERR_OR_NULL(panel)) {
+ bdata->panel = panel;
+ bdata->initial_panel_index = panelmap.args[0];
+ break;
+ }
+ }
+ }
+
+ bdata->switch_gpio = of_get_named_gpio(np, "st,switch_gpio", 0);
+ pr_info("switch_gpio = %d\n", bdata->switch_gpio);
+
+ bdata->irq_gpio = of_get_named_gpio_flags(np, "st,irq-gpio", 0, NULL);
+ pr_info("irq_gpio = %d\n", bdata->irq_gpio);
+
+ retval = of_property_read_string(np, "st,regulator_dvdd", &name);
+ if (retval == -EINVAL)
+ bdata->vdd_reg_name = NULL;
+ else if (retval < 0)
+ return retval;
+ else {
+ bdata->vdd_reg_name = name;
+ pr_info("pwr_reg_name = %s\n", name);
+ }
+
+ retval = of_property_read_string(np, "st,regulator_avdd", &name);
+ if (retval == -EINVAL)
+ bdata->avdd_reg_name = NULL;
+ else if (retval < 0)
+ return retval;
+ else {
+ bdata->avdd_reg_name = name;
+ pr_info("bus_reg_name = %s\n", name);
+ }
+
+ if (of_property_read_bool(np, "st,reset-gpio")) {
+ bdata->reset_gpio = of_get_named_gpio_flags(np,
+ "st,reset-gpio", 0,
+ NULL);
+ pr_info("reset_gpio = %d\n", bdata->reset_gpio);
+ } else
+ bdata->reset_gpio = GPIO_NOT_DEFINED;
+
+ if (of_property_read_bool(np, "st,disp-rate-gpio")) {
+ bdata->disp_rate_gpio =
+ of_get_named_gpio_flags(np, "st,disp-rate-gpio", 0, NULL);
+ pr_info("disp_rate_gpio = %d\n", bdata->disp_rate_gpio);
+ } else
+ bdata->disp_rate_gpio = GPIO_NOT_DEFINED;
+
+ bdata->auto_fw_update = true;
+ if (of_property_read_bool(np, "st,disable-auto-fw-update")) {
+ bdata->auto_fw_update = false;
+ pr_info("Automatic firmware update disabled\n");
+ }
+
+#ifdef TOUCHSCREEN_HEATMAP
+ bdata->heatmap_mode_full_init = false;
+ if (of_property_read_bool(np, "st,heatmap_mode_full")) {
+ bdata->heatmap_mode_full_init = true;
+ pr_info("Full heatmap enabled\n");
+ }
+#endif
+
+ if (panel && panel->funcs && panel->funcs->get_timings &&
+ panel->funcs->get_timings(panel, 1, &timing) > 0) {
+ coords[0] = timing.hactive.max - 1;
+ coords[1] = timing.vactive.max - 1;
+ } else if (of_property_read_u32_array(np, "st,max-coords", coords, 2)) {
+ pr_err("st,max-coords not found, using 1440x2560\n");
+ coords[0] = 1440 - 1;
+ coords[1] = 2560 - 1;
+ }
+ bdata->x_axis_max = coords[0];
+ bdata->y_axis_max = coords[1];
+
+ return OK;
+}
+
+/**
+ * Probe function, called when the driver it is matched with a device
+ * with the same name compatible name
+ * This function allocate, initialize all the most important functions and flow
+ * those are used by the driver to operate with the IC.
+ * It allocates device variables, initialize queues and schedule works,
+ * registers the IRQ handler, suspend/resume callbacks, registers the device
+ * to the linux input subsystem etc.
+ */
+#ifdef I2C_INTERFACE
+static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp)
+{
+#else
+static int fts_probe(struct spi_device *client)
+{
+#endif
+
+ struct fts_ts_info *info = NULL;
+ int error = 0;
+ struct device_node *dp = client->dev.of_node;
+ int retval;
+ int skip_5_1 = 0;
+ u16 bus_type;
+
+ pr_info("%s: driver probe begin!\n", __func__);
+ pr_info("driver ver. %s\n", FTS_TS_DRV_VERSION);
+
+ pr_info("SET Bus Functionality :\n");
+#ifdef I2C_INTERFACE
+ pr_info("I2C interface...\n");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("Unsupported I2C functionality\n");
+ error = -EIO;
+ goto ProbeErrorExit_0;
+ }
+
+ pr_info("i2c address: %x\n", client->addr);
+ bus_type = BUS_I2C;
+#else
+ pr_info("SPI interface...\n");
+
+ client->bits_per_word = 8;
+ if (spi_setup(client) < 0) {
+ pr_err("Unsupported SPI functionality\n");
+ error = -EIO;
+ goto ProbeErrorExit_0;
+ }
+ bus_type = BUS_SPI;
+#endif
+
+
+ pr_info("SET Device driver INFO:\n");
+
+
+ info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL);
+ if (!info) {
+ pr_err("Out of memory... Impossible to allocate struct info!\n");
+ error = -ENOMEM;
+ goto ProbeErrorExit_0;
+ }
+
+ info->client = client;
+ info->dev = &info->client->dev;
+
+#ifdef DYNAMIC_REFRESH_RATE
+ /* Set default display refresh rate */
+ info->display_refresh_rate = 60;
+#endif
+
+ dev_set_drvdata(info->dev, info);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ info->tbn = tbn_init(info->dev);
+ if (!info->tbn) {
+ pr_err("ERROR: failed to init tbn context\n");
+ error = -ENODEV;
+ goto ProbeErrorExit_1;
+ }
+#endif
+
+ if (dp) {
+ info->board = devm_kzalloc(&client->dev,
+ sizeof(struct fts_hw_platform_data),
+ GFP_KERNEL);
+ if (!info->board) {
+ pr_err("ERROR:info.board kzalloc failed\n");
+ goto ProbeErrorExit_1;
+ }
+ error = parse_dt(&client->dev, info->board);
+ if (error)
+ goto ProbeErrorExit_1;
+ }
+
+ pr_info("SET Regulators:\n");
+ retval = fts_get_reg(info, true);
+ if (retval < 0) {
+ pr_err("ERROR: %s: Failed to get regulators\n", __func__);
+ goto ProbeErrorExit_1;
+ }
+
+ retval = fts_enable_reg(info, true);
+ if (retval < 0) {
+ pr_err("%s: ERROR Failed to enable regulators\n", __func__);
+ goto ProbeErrorExit_2;
+ }
+
+ pr_info("SET GPIOS:\n");
+ retval = fts_set_gpio(info);
+ if (retval < 0) {
+ pr_err("%s: ERROR Failed to set up GPIO's\n", __func__);
+ goto ProbeErrorExit_2;
+ }
+ info->client->irq = gpio_to_irq(info->board->irq_gpio);
+
+ pr_info("SET Pinctrl:\n");
+ retval = fts_pinctrl_get(info, true);
+ if (!retval)
+ fts_pinctrl_setup(info, true);
+
+ pr_info("SET Event Handler:\n");
+
+ info->wakesrc = wakeup_source_register(NULL, "fts_tp");
+ if (!info->wakesrc) {
+ pr_err("%s: failed to register wakeup source\n", __func__);
+ error = -ENODEV;
+ goto ProbeErrorExit_3;
+
+ }
+ info->event_wq = alloc_workqueue("fts-event-queue", WQ_UNBOUND |
+ WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1);
+ if (!info->event_wq) {
+ pr_err("ERROR: Cannot create work thread\n");
+ error = -ENOMEM;
+ goto ProbeErrorExit_4;
+ }
+
+ INIT_WORK(&info->resume_work, fts_resume_work);
+ INIT_WORK(&info->suspend_work, fts_suspend_work);
+
+ init_completion(&info->bus_resumed);
+ complete_all(&info->bus_resumed);
+
+ pr_info("SET Input Device Property:\n");
+ info->dev = &info->client->dev;
+ info->input_dev = input_allocate_device();
+ if (!info->input_dev) {
+ pr_err("ERROR: No such input device defined!\n");
+ error = -ENODEV;
+ goto ProbeErrorExit_5;
+ }
+ info->input_dev->dev.parent = &client->dev;
+ info->input_dev->name = FTS_TS_DRV_NAME;
+ scnprintf(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_type;
+ 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); */
+ /* __set_bit(BTN_TOOL_PEN, info->input_dev->keybit); */
+
+ input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT);
+
+ /* input_mt_init_slots(info->input_dev, TOUCH_ID_MAX); */
+
+ input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, X_AXIS_MIN,
+ info->board->x_axis_max, 0, 0);
+ input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, Y_AXIS_MIN,
+ info->board->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);
+ input_set_abs_params(info->input_dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER,
+ MT_TOOL_FINGER, 0, 0);
+#ifndef SKIP_PRESSURE
+ input_set_abs_params(info->input_dev, ABS_MT_PRESSURE, PRESSURE_MIN,
+ PRESSURE_MAX, 0, 0);
+#endif
+#ifndef SKIP_DISTANCE
+ input_set_abs_params(info->input_dev, ABS_MT_DISTANCE, DISTANCE_MIN,
+ DISTANCE_MAX, 0, 0);
+#endif
+
+#ifdef GESTURE_MODE
+ 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->diag_cmd_lock);
+
+ mutex_init(&(info->input_report_mutex));
+ mutex_init(&info->bus_mutex);
+
+ /* Assume screen is on throughout probe */
+ info->bus_refmask = FTS_BUS_REF_SCREEN_ON;
+
+#ifdef GESTURE_MODE
+ mutex_init(&gestureMask_mutex);
+#endif
+
+ spin_lock_init(&info->fts_int);
+
+ /* register the multi-touch input device */
+ error = input_register_device(info->input_dev);
+ if (error) {
+ pr_err("ERROR: No such input device\n");
+ error = -ENODEV;
+ goto ProbeErrorExit_5_1;
+ }
+
+ skip_5_1 = 1;
+ /* track slots */
+ info->touch_id = 0;
+ info->palm_touch_mask = 0;
+ info->grip_touch_mask = 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->cover_enabled = 0;
+ info->grip_enabled = 0;
+
+ info->resume_bit = 1;
+#ifdef CONFIG_DRM
+ info->notifier = fts_noti_block;
+#endif
+
+ /* Set initial heatmap mode based on the device tree configuration.
+ * Default is partial heatmap mode.
+ */
+#ifdef TOUCHSCREEN_HEATMAP
+ if (info->board->heatmap_mode_full_init)
+ info->heatmap_mode = FTS_HEATMAP_FULL;
+ else
+ info->heatmap_mode = FTS_HEATMAP_PARTIAL;
+#endif
+
+ /* init motion filter mode */
+ info->use_default_mf = false;
+
+ /*
+ * This *must* be done before request_threaded_irq is called.
+ * Otherwise, if an interrupt is received before request is added,
+ * but after the interrupt has been subscribed to, pm_qos_req
+ * may be accessed before initialization in the interrupt handler.
+ */
+ pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ pr_info("Init Core Lib:\n");
+ initCore(info);
+ /* init hardware device */
+ pr_info("Device Initialization:\n");
+ error = fts_init(info);
+ if (error < OK) {
+ pr_err("Cannot initialize the device ERROR %08X\n", error);
+ error = -ENODEV;
+ goto ProbeErrorExit_6;
+ }
+
+#ifdef TOUCHSCREEN_HEATMAP
+ /*
+ * Heatmap_probe must be called before irq routine is registered,
+ * because heatmap_read is called from interrupt context.
+ * This is done as part of fwu_work.
+ * At the same time, heatmap_probe must be done after fts_init(..) has
+ * completed, because getForceLen() and getSenseLen() require
+ * the chip to be initialized.
+ */
+ info->v4l2.parent_dev = info->dev;
+ info->v4l2.input_dev = info->input_dev;
+ info->v4l2.read_frame = read_heatmap_raw;
+ info->v4l2.width = getForceLen();
+ info->v4l2.height = getSenseLen();
+ /* 120 Hz operation */
+ info->v4l2.timeperframe.numerator = 1;
+ info->v4l2.timeperframe.denominator = 120;
+ error = heatmap_probe(&info->v4l2);
+ if (error < OK)
+ goto ProbeErrorExit_6;
+#endif
+
+#if defined(FW_UPDATE_ON_PROBE) && defined(FW_H_FILE)
+ pr_info("FW Update and Sensing Initialization:\n");
+ error = fts_fw_update(info);
+ if (error < OK) {
+ pr_err("Cannot execute fw upgrade the device ERROR %08X\n",
+ error);
+ error = -ENODEV;
+ goto ProbeErrorExit_7;
+ }
+
+#else
+ pr_info("SET Auto Fw Update:\n");
+ info->fwu_workqueue = alloc_workqueue("fts-fwu-queue",
+ WQ_UNBOUND | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE, 1);
+ if (!info->fwu_workqueue) {
+ pr_err("ERROR: Cannot create fwu work thread\n");
+ goto ProbeErrorExit_7;
+ }
+ INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto);
+#endif
+
+ pr_info("SET Device File Nodes:\n");
+ /* sysfs stuff */
+ info->attrs.attrs = fts_attr_group;
+ error = sysfs_create_group(&client->dev.kobj, &info->attrs);
+ if (error) {
+ pr_err("ERROR: Cannot create sysfs structure!\n");
+ error = -ENODEV;
+ goto ProbeErrorExit_7;
+ }
+
+ error = fts_proc_init();
+ if (error < OK)
+ pr_err("Error: can not create /proc file!\n");
+
+ if (info->fwu_workqueue)
+ queue_delayed_work(info->fwu_workqueue, &info->fwu_work,
+ msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
+
+ info->touchsim.wq = alloc_workqueue("fts-heatmap_test-queue",
+ WQ_UNBOUND | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE, 1);
+
+ if (info->touchsim.wq)
+ INIT_WORK(&(info->touchsim.work), touchsim_work);
+ else
+ pr_err("ERROR: Cannot create touch sim. test work queue\n");
+
+ pr_info("Probe Finished!\n");
+
+ return OK;
+
+
+ProbeErrorExit_7:
+ if(info->touchsim.wq)
+ destroy_workqueue(info->touchsim.wq);
+
+#ifdef CONFIG_DRM
+ drm_panel_notifier_unregister(info->board->panel, &info->notifier);
+#endif
+
+#ifdef TOUCHSCREEN_HEATMAP
+ heatmap_remove(&info->v4l2);
+#endif
+
+ProbeErrorExit_6:
+ pm_qos_remove_request(&info->pm_qos_req);
+ 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->wakesrc);
+
+ProbeErrorExit_3:
+ fts_pinctrl_get(info, false);
+
+ fts_enable_reg(info, false);
+
+ProbeErrorExit_2:
+ fts_get_reg(info, false);
+
+ProbeErrorExit_1:
+ kfree(info);
+
+ProbeErrorExit_0:
+ if (error != -EPROBE_DEFER)
+ pr_err("Probe Failed!\n");
+
+ return error;
+}
+
+
+/**
+ * Clear and free all the resources associated to the driver.
+ * This function is called when the driver need to be removed.
+ */
+#ifdef I2C_INTERFACE
+static int fts_remove(struct i2c_client *client)
+{
+#else
+static int fts_remove(struct spi_device *client)
+{
+#endif
+
+ struct fts_ts_info *info = dev_get_drvdata(&(client->dev));
+
+ /* Force the bus active throughout removal of the client */
+ fts_set_bus_ref(info, FTS_BUS_REF_FORCE_ACTIVE, true);
+
+ pr_info("%s\n", __func__);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ tbn_cleanup(info->tbn);
+#endif
+
+ fts_proc_remove();
+
+ /* sysfs stuff */
+ sysfs_remove_group(&client->dev.kobj, &info->attrs);
+
+ /* remove interrupt and event handlers */
+ fts_interrupt_uninstall(info);
+
+#ifdef TOUCHSCREEN_HEATMAP
+ heatmap_remove(&info->v4l2);
+#endif
+
+ pm_qos_remove_request(&info->pm_qos_req);
+
+#ifdef CONFIG_DRM
+ drm_panel_notifier_unregister(info->board->panel, &info->notifier);
+#endif
+
+ /* unregister the device */
+ input_unregister_device(info->input_dev);
+
+ /* input_free_device(info->input_dev ); */
+
+ /* Remove the work thread */
+ destroy_workqueue(info->event_wq);
+ wakeup_source_unregister(info->wakesrc);
+
+ if(info->touchsim.wq)
+ destroy_workqueue(info->touchsim.wq);
+
+ if (info->fwu_workqueue)
+ destroy_workqueue(info->fwu_workqueue);
+
+ fts_pinctrl_get(info, false);
+
+ fts_enable_reg(info, false);
+ fts_get_reg(info, false);
+
+ /* free gpio */
+ if (gpio_is_valid(info->board->irq_gpio))
+ gpio_free(info->board->irq_gpio);
+ if (gpio_is_valid(info->board->switch_gpio))
+ gpio_free(info->board->switch_gpio);
+ if (gpio_is_valid(info->board->reset_gpio))
+ gpio_free(info->board->reset_gpio);
+ if (gpio_is_valid(info->board->disp_rate_gpio))
+ gpio_free(info->board->disp_rate_gpio);
+
+ /* free any extinfo */
+ kfree(info->extinfo.data);
+
+ /* free all */
+ kfree(info);
+
+ return OK;
+}
+
+#ifdef CONFIG_PM
+static int fts_pm_suspend(struct device *dev)
+{
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+
+ if (info->bus_refmask)
+ pr_warn("%s: bus_refmask 0x%X\n", __func__, info->bus_refmask);
+
+ if (info->resume_bit == 1 || info->sensor_sleep == false) {
+#ifdef SUPPORT_PROX_PALM
+ /* Don't block CPU suspend during phone call*/
+ if (info->bus_refmask == FTS_BUS_REF_PHONE_CALL) {
+ fts_enableInterrupt(false);
+ enable_irq_wake(info->client->irq);
+ return 0;
+ }
+#endif
+ pr_warn("%s: can't suspend because touch bus is in use!\n",
+ __func__);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int fts_pm_resume(struct device *dev)
+{
+#ifdef SUPPORT_PROX_PALM
+ struct fts_ts_info *info = dev_get_drvdata(dev);
+ if (info->bus_refmask == FTS_BUS_REF_PHONE_CALL) {
+ fts_enableInterrupt(true);
+ disable_irq_wake(info->client->irq);
+ }
+#endif
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(fts_pm_ops, fts_pm_suspend, fts_pm_resume);
+#endif
+
+/**
+ * Struct which contains the compatible names that need to match with
+ * the definition of the device in the device tree node
+ */
+static struct of_device_id fts_of_match_table[] = {
+ {
+ .compatible = "st,fts",
+ },
+ {},
+};
+
+#ifdef I2C_INTERFACE
+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,
+#ifdef CONFIG_PM
+ .pm = &fts_pm_ops,
+#endif
+ },
+ .probe = fts_probe,
+ .remove = fts_remove,
+ .id_table = fts_device_id,
+};
+#else
+static struct spi_driver fts_spi_driver = {
+ .driver = {
+ .name = FTS_TS_DRV_NAME,
+ .of_match_table = fts_of_match_table,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &fts_pm_ops,
+#endif
+ },
+ .probe = fts_probe,
+ .remove = fts_remove,
+};
+#endif
+
+
+
+
+static int __init fts_driver_init(void)
+{
+#ifdef I2C_INTERFACE
+ return i2c_add_driver(&fts_i2c_driver);
+#else
+ return spi_register_driver(&fts_spi_driver);
+#endif
+}
+
+static void __exit fts_driver_exit(void)
+{
+ pr_info("%s\n", __func__);
+#ifdef I2C_INTERFACE
+ i2c_del_driver(&fts_i2c_driver);
+#else
+ spi_unregister_driver(&fts_spi_driver);
+#endif
+}
+
+
+MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver");
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_LICENSE("GPL v2");
+
+late_initcall(fts_driver_init);
+module_exit(fts_driver_exit);
diff --git a/fts.h b/fts.h
new file mode 100644
index 0000000..3b3e0b2
--- /dev/null
+++ b/fts.h
@@ -0,0 +1,531 @@
+/*
+ * fts.h
+ *
+ * FTS Capacitive touch screen controller (FingerTipS)
+ *
+ * Copyright (C) 2017, STMicroelectronics
+ * 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.
+ *
+ * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
+ * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
+ * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM
+ * THE
+ * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+ * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ */
+
+/*!
+ * \file fts.h
+ * \brief Contains all the definitions and structs used generally by the driver
+ */
+
+#ifndef _LINUX_FTS_I2C_H_
+#define _LINUX_FTS_I2C_H_
+
+#define TOUCHSCREEN_HEATMAP
+
+#include <linux/device.h>
+#ifdef TOUCHSCREEN_HEATMAP
+#include <linux/input/heatmap.h>
+#endif
+#include <linux/pm_qos.h>
+#include <drm/drm_panel.h>
+#include "fts_lib/ftsSoftware.h"
+#include "fts_lib/ftsHardware.h"
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+#include <linux/input/touch_bus_negotiator.h>
+#endif
+
+#undef DYNAMIC_REFRESH_RATE
+
+/****************** CONFIGURATION SECTION ******************/
+/** @defgroup conf_section Driver Configuration Section
+ * Settings of the driver code in order to suit the HW set up and the
+ *application behavior
+ * @{
+ */
+/* **** CODE CONFIGURATION **** */
+#define FTS_TS_DRV_NAME "fts" /* driver name */
+#define FTS_TS_DRV_VERSION "5.2.16.14" /* driver version string
+ * */
+#define FTS_TS_DRV_VER 0x0502100E /* driver version u32 format */
+
+/* #define DEBUG */ /* /< define to print more logs in the kernel log
+ * and better follow the code flow */
+#ifdef pr_fmt
+#undef pr_fmt
+#define pr_fmt(fmt) "[ FTS ] " fmt
+#endif
+
+#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE "pmx_ts_release"
+
+#define DRIVER_TEST /* /< if defined allow to use and test special functions
+ * of the driver and fts_lib from command shell
+ * (useful for enginering/debug operations) */
+
+/* If both COMPUTE_INIT_METHOD and PRE_SAVED_METHOD are not defined,
+ * driver will be automatically configured as GOLDEN_VALUE_METHOD
+ */
+/*#define COMPUTE_INIT_METHOD Allow to compute init data on phone during
+ * production
+ */
+#define SKIP_PRODUCTION_TEST /* Allow to skip Production test */
+
+//#ifndef COMPUTE_INIT_METHOD
+/*#define PRE_SAVED_METHOD Pre-Saved Method used
+ * during production
+ */
+//#endif
+
+/*#define FW_H_FILE*/ /* include the FW data as header file */
+#ifdef FW_H_FILE
+#define FW_SIZE_NAME myArray_size /* FW data array size */
+#define FW_ARRAY_NAME myArray /* FW data array name */
+/*#define FW_UPDATE_ON_PROBE*/ /* No delay updating FW */
+#endif
+
+#ifndef FW_UPDATE_ON_PROBE
+/* Include the Production Limit File as header file, can be commented to use a
+ * .csv file instead */
+/* #define LIMITS_H_FILE */
+#ifdef LIMITS_H_FILE
+ #define LIMITS_SIZE_NAME myArray2_size /* /< name of the
+ * variable
+ * in the limits header
+ *file which
+ * specified the
+ *dimension of
+ * the limits data
+ *array */
+ #define LIMITS_ARRAY_NAME myArray2 /* /< name of the
+ * variable in
+ * the limits header
+ *file which
+ * specified the limits
+ *data array */
+#endif
+#else
+/* if execute fw update in the probe the limit file must be a .h */
+#define LIMITS_H_FILE /* /< include the Production Limit File as header file,
+ * DO NOT COMMENT! */
+#define LIMITS_SIZE_NAME myArray2_size /* /< name of the
+ * variable
+ * in the limits header
+ *file
+ * which specified the
+ *dimension
+ * of the limits data
+ *array */
+#define LIMITS_ARRAY_NAME myArray2 /* /< name of the
+ * variable in the
+ * limits header file
+ *which specified
+ * the limits data
+ *array */
+#endif
+
+/* #define USE_ONE_FILE_NODE */ /* /< allow to enable/disable all the features
+ * just using one file node */
+
+#ifndef FW_UPDATE_ON_PROBE
+#define EXP_FN_WORK_DELAY_MS 1000 /* /< time in ms elapsed after the probe
+ * to start the work which execute FW
+ *update
+ * and the Initialization of the IC */
+#endif
+
+/* **** END **** */
+
+
+/* **** FEATURES USED IN THE IC **** */
+/* Enable the support of keys */
+/* #define PHONE_KEY */
+
+#define GESTURE_MODE /* /< enable the support of the gestures */
+#ifdef GESTURE_MODE
+ #define USE_GESTURE_MASK /* /< the gestures to select are
+ * referred using
+ * a gesture bitmask instead of their
+ *gesture IDs */
+#endif
+
+
+#define CHARGER_MODE /* /< enable the support to charger mode feature
+ * (comment to disable) */
+
+#define GLOVE_MODE /* /< enable the support to glove mode feature (comment
+ * to disable) */
+
+#define COVER_MODE /* /< enable the support to cover mode feature (comment
+ * to disable) */
+
+#define STYLUS_MODE /* /< enable the support to stylus mode feature (comment
+ * to disable) */
+
+#define GRIP_MODE /* /< enable the support to grip mode feature (comment
+ * to disable) */
+
+
+/* **** END **** */
+
+
+/* **** PANEL SPECIFICATION **** */
+#define X_AXIS_MIN 0 /* /< min X coordinate of the display */
+#define Y_AXIS_MIN 0 /* /< min Y coordinate of the display */
+#define Y_AXIS_MAX 2959 /* /< Max Y coordinate of the display */
+#define X_AXIS_MAX 1440 /* /< Max X coordinate of the display */
+
+#define PRESSURE_MIN 0 /* /< min value of pressure reported */
+#define PRESSURE_MAX 127 /* /< Max value of pressure reported */
+
+#define DISTANCE_MIN 0 /* /< min distance between the tool and the
+ * display */
+#define DISTANCE_MAX 127 /* /< Max distance between the tool and the
+ * display */
+
+#define TOUCH_ID_MAX 10 /* /< Max number of simoultaneous touches
+ * reported */
+
+#define AREA_MIN PRESSURE_MIN /* /< min value of Major/minor axis
+ * reported */
+#define AREA_MAX PRESSURE_MAX /* /< Man value of Major/minor axis
+ * reported */
+/* **** END **** */
+
+/* #define SKIP_PRESSURE */
+
+/**@}*/
+/*********************************************************/
+#ifdef TOUCHSCREEN_HEATMAP
+/* **** LOCAL HEATMAP FEATURE *** */
+#define LOCAL_HEATMAP_WIDTH 7
+#define LOCAL_HEATMAP_HEIGHT 7
+#define LOCAL_HEATMAP_MODE 0xC1
+
+struct heatmap_report {
+ uint8_t prefix; /* always should be 0xA0 */
+ uint8_t mode; /* mode should be 0xC1 for heatmap */
+
+ uint16_t counter; /* LE order, should increment on each heatmap read */
+ int8_t offset_x;
+ uint8_t size_x;
+ int8_t offset_y;
+ uint8_t size_y;
+ /* data is in LE order; order should be enforced after data is read */
+ strength_t data[LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT];
+} __attribute__((packed));
+/* **** END **** */
+#endif
+/*
+ * Configuration mode
+ *
+ * bitmask which can assume the value defined as features in ftsSoftware.h or
+ * the following values
+ */
+
+/** @defgroup mode_section IC Status Mode
+ * Bitmask which keeps track of the features and working mode enabled in the
+ * IC.
+ * The meaning of the the LSB of the bitmask must be interpreted considering
+ * that the value defined in @link feat_opt Feature Selection Option @endlink
+ * correspond to the position of the corresponding bit in the mask
+ * @{
+ */
+#define MODE_NOTHING 0x00000000 /* /< nothing enabled (sense off) */
+#define MODE_ACTIVE(_mask, _sett) \
+ (_mask |= (SCAN_MODE_ACTIVE << 24) | (_sett << 16))
+/* /< store the status of scan mode active and its setting */
+#define MODE_LOW_POWER(_mask, _sett) \
+ (_mask |= (SCAN_MODE_LOW_POWER << 24) | (_sett << 16))
+/* /< store the status of scan mode low power and its setting */
+#define IS_POWER_MODE(_mask, _mode) ((_mask&(_mode<<24)) != 0x00)
+/* /< check the current mode of the IC */
+
+/** @}*/
+
+#define CMD_STR_LEN 32 /* /< max number of parameters that can accept
+ * the
+ * MP file node (stm_fts_cmd) */
+
+#define TSP_BUF_SIZE PAGE_SIZE /* /< max number of bytes printable on
+ * the shell in the normal file nodes
+ **/
+
+/* Encapsulate display extinfo
+ *
+ * For some panels, it is insufficient to simply detect the panel ID and load
+ * one corresponding firmware. The display driver exposes extended info read
+ * from the display, but it is up to the touch driver to parse the data.
+ */
+struct fts_disp_extinfo {
+ bool is_read;
+ u8 size;
+ u8 *data;
+};
+
+/**
+ * Struct which contains information about the HW platform and set up
+ */
+struct fts_hw_platform_data {
+ int (*power) (bool on);
+ int switch_gpio;/* (optional) I2C switch */
+ int irq_gpio; /* /< number of the gpio associated to the interrupt pin
+ * */
+ int reset_gpio; /* /< number of the gpio associated to the reset pin */
+ int disp_rate_gpio; /* disp_rate gpio: LOW=60Hz, HIGH=90Hz */
+ const char *vdd_reg_name; /* /< name of the VDD regulator */
+ const char *avdd_reg_name; /* /< name of the AVDD regulator */
+ const char *fw_name;
+ const char *limits_name;
+ bool sensor_inverted;
+ int x_axis_max;
+ int y_axis_max;
+ bool auto_fw_update;
+#ifdef TOUCHSCREEN_HEATMAP
+ bool heatmap_mode_full_init;
+#endif
+ struct drm_panel *panel;
+ u32 initial_panel_index;
+ u32 *force_pi_cfg_ver;
+};
+
+/* Bits for the bus reference mask */
+enum {
+ FTS_BUS_REF_SCREEN_ON = 0x01,
+ FTS_BUS_REF_IRQ = 0x02,
+ FTS_BUS_REF_FW_UPDATE = 0x04,
+ FTS_BUS_REF_SYSFS = 0x08,
+ FTS_BUS_REF_FORCE_ACTIVE = 0x10,
+#ifdef SUPPORT_PROX_PALM
+ FTS_BUS_REF_PHONE_CALL = 0x20,
+#endif
+};
+
+/* Motion filter finite state machine (FSM) states
+ * FTS_MF_FILTERED - default coordinate filtering
+ * FTS_MF_UNFILTERED - unfiltered single-touch coordinates
+ * FTS_MF_FILTERED_LOCKED - filtered coordinates. Locked until touch is lifted.
+ */
+typedef enum {
+ FTS_MF_FILTERED = 0,
+ FTS_MF_UNFILTERED = 1,
+ FTS_MF_FILTERED_LOCKED = 2
+} motion_filter_state_t;
+
+/* Heatmap mode selection
+ * FTS_HEATMAP_OFF - no data read
+ * FTS_HEATMAP_PARTIAL - read partial frame
+ * (LOCAL_HEATMAP_WIDTH * LOCAL_HEATMAP_HEIGHT)
+ * FTS_HEATMAP_FULL - read full mutual sense strength frame
+ */
+#ifdef TOUCHSCREEN_HEATMAP
+enum {
+ FTS_HEATMAP_OFF = 0,
+ FTS_HEATMAP_PARTIAL = 1,
+ FTS_HEATMAP_FULL = 2
+};
+#endif
+/*
+ * Forward declaration
+ */
+struct fts_ts_info;
+
+/*
+ * Dispatch event handler
+ * Return true if the handler has processed a pointer event
+ */
+typedef bool (*event_dispatch_handler_t)
+ (struct fts_ts_info *info, unsigned char *data);
+
+/**
+ * Driver touch simulation details
+ */
+struct fts_touchsim{
+ /* touch simulation coordinates */
+ int x, y, x_step, y_step;
+
+ /* timer to run the touch simulation code */
+ struct hrtimer hr_timer;
+
+ struct work_struct work;
+ struct workqueue_struct *wq;
+
+ /* True if the touch simulation is currently running */
+ bool is_running;
+};
+
+/**
+ * FTS capacitive touch screen device information
+ * - dev Pointer to the structure device \n
+ * - client client structure \n
+ * - input_dev Input device structure \n
+ * - work Work thread \n
+ * - event_wq Event queue for work thread \n
+ * - event_dispatch_table Event dispatch table handlers \n
+ * - attrs SysFS attributes \n
+ * - mode Device operating mode (bitmask) \n
+ * - touch_id Bitmask for touch id (mapped to input slots) \n
+ * - stylus_id Bitmask for tracking the stylus touches (mapped using the
+ * touchId) \n
+ * - timer Timer when operating in polling mode \n
+ * - power Power on/off routine \n
+ * - board HW info retrieved from device tree \n
+ * - vdd_reg DVDD power regulator \n
+ * - avdd_reg AVDD power regulator \n
+ * - resume_bit Indicate if screen off/on \n
+ * - fwupdate_stat Store the result of a fw update triggered by the host \n
+ * - notifier Used for be notified from a suspend/resume event \n
+ * - sensor_sleep true suspend was called, false resume was called \n
+ * - wakesrc Wakeup Source struct \n
+ * - input_report_mutex mutex for handling the pressure of keys \n
+ * - series_of_switches to store the enabling status of a particular feature
+ * from the host \n
+ * - tbn Touch Bus Negotiator context
+ */
+struct fts_ts_info {
+ struct device *dev; /* Pointer to the device */
+#ifdef I2C_INTERFACE
+ struct i2c_client *client; /* I2C client structure */
+#else
+ struct spi_device *client; /* SPI client structure */
+#endif
+ struct input_dev *input_dev; /* Input device structure */
+
+ struct work_struct suspend_work; /* Suspend work thread */
+ struct work_struct resume_work; /* Resume work thread */
+ struct workqueue_struct *event_wq; /* Used for event handler, */
+ /* suspend, resume threads */
+
+ struct completion bus_resumed; /* resume_work complete */
+
+ struct pm_qos_request pm_qos_req;
+#ifdef TOUCHSCREEN_HEATMAP
+ struct v4l2_heatmap v4l2;
+#endif
+ struct delayed_work fwu_work; /* Work for fw update */
+ struct workqueue_struct *fwu_workqueue; /* Fw update work queue */
+ event_dispatch_handler_t *event_dispatch_table; /* Dispatch table */
+
+ struct attribute_group attrs; /* SysFS attributes */
+
+ unsigned int mode; /* Device operating mode */
+ /* MSB - active or lpm */
+ unsigned long touch_id; /* Bitmask for touch id */
+ unsigned long palm_touch_mask; /* Bitmask for palm touch */
+ unsigned long grip_touch_mask; /* Bitmask for grip touch */
+#ifdef STYLUS_MODE
+ unsigned long stylus_id; /* Bitmask for the stylus */
+#endif
+
+ ktime_t timestamp; /* time that the event was first received from the
+ touch IC, acquired during hard interrupt, in CLOCK_MONOTONIC */
+
+ struct fts_hw_platform_data *board; /* HW info from device tree */
+ struct regulator *vdd_reg; /* DVDD power regulator */
+ struct regulator *avdd_reg; /* AVDD power regulator */
+
+ struct pinctrl *ts_pinctrl; /* touch pin control state holder */
+ struct pinctrl_state *pinctrl_state_active; /* Active pin state*/
+ struct pinctrl_state *pinctrl_state_suspend; /* Suspend pin state*/
+ struct pinctrl_state *pinctrl_state_release; /* Release pin state*/
+
+ spinlock_t fts_int; /* Spinlock to protect interrupt toggling */
+ bool irq_enabled; /* Interrupt state */
+
+ struct mutex bus_mutex; /* Protect access to the bus */
+ unsigned int bus_refmask; /* References to the bus */
+
+ int resume_bit; /* Indicate if screen off/on */
+ int fwupdate_stat; /* Result of a fw update */
+ int reflash_fw; /* Attempt to reflash fw */
+ int autotune_stat; /* Attempt to autotune */
+
+ struct fts_disp_extinfo extinfo; /* Display extended info */
+
+#ifdef CONFIG_DRM
+ struct notifier_block notifier; /* Notify on suspend/resume */
+#endif
+#ifdef DYNAMIC_REFRESH_RATE
+ int display_refresh_rate; /* Display rate in Hz */
+#endif
+ bool sensor_sleep; /* True if suspend called */
+ struct wakeup_source *wakesrc; /* Wake Lock struct */
+
+ /* input lock */
+ struct mutex input_report_mutex; /* Mutex for pressure report */
+
+ /* switches for features */
+ int gesture_enabled; /* Gesture during suspend */
+ int glove_enabled; /* Glove mode */
+ int charger_enabled; /* Charger mode */
+ int stylus_enabled; /* Stylus mode */
+ int cover_enabled; /* Cover mode */
+ int grip_enabled; /* Grip mode */
+#ifdef TOUCHSCREEN_HEATMAP
+ int heatmap_mode; /* heatmap mode*/
+#endif
+#ifdef SUPPORT_PROX_PALM
+ int audio_status;
+ int prox_palm_status;
+#endif
+ /* Stop changing motion filter and keep fw design */
+ bool use_default_mf;
+ /* Motion filter finite state machine (FSM) state */
+ motion_filter_state_t mf_state;
+ /* Time of initial single-finger touch down. This timestamp is used to
+ * compute the duration a single finger is touched before it is lifted.
+ */
+ ktime_t mf_downtime;
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN)
+ struct tbn_context *tbn;
+#endif
+
+ /* Allow only one thread to execute diag command code*/
+ struct mutex diag_cmd_lock;
+ /* Allow one process to open procfs node */
+ bool diag_node_open;
+
+ /* Touch simulation details */
+ struct fts_touchsim touchsim;
+
+ u8 scanning_frequency;
+
+ /* Preallocated i/o read buffer */
+ u8 io_read_buf[READ_CHUNK + DUMMY_FIFO];
+ /* Preallocated i/o write buffer */
+ u8 io_write_buf[WRITE_CHUNK + BITS_64 + DUMMY_FIFO];
+ /* Preallocated i/o extra write buffer */
+ u8 io_extra_write_buf[WRITE_CHUNK + BITS_64 + DUMMY_FIFO];
+
+};
+
+/* DSI display function used to read panel extinfo */
+int dsi_panel_read_vendor_extinfo(struct drm_panel *panel, char *buffer,
+ size_t len);
+
+int fts_chip_powercycle(struct fts_ts_info *info);
+extern int input_register_notifier_client(struct notifier_block *nb);
+extern int input_unregister_notifier_client(struct notifier_block *nb);
+
+/* export declaration of functions in fts_proc.c */
+extern int fts_proc_init(void);
+extern int fts_proc_remove(void);
+
+/* Bus reference tracking */
+int fts_set_bus_ref(struct fts_ts_info *info, u16 ref, bool enable);
+
+#endif
diff --git a/fts_lib/ftsCompensation.c b/fts_lib/ftsCompensation.c
new file mode 100644
index 0000000..24fc227
--- /dev/null
+++ b/fts_lib/ftsCompensation.c
@@ -0,0 +1,1123 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS functions for getting Initialization Data *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+/*!
+ * \file ftsCompensation.c
+ * \brief Contains all the function to work with Initialization Data
+ */
+
+#include "ftsCompensation.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsFrame.h"
+#include "ftsHardware.h"
+#include "ftsIO.h"
+#include "ftsSoftware.h"
+#include "ftsTime.h"
+#include "ftsTool.h"
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/serio.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+
+/**
+ * Request to the FW to load the specified Initialization Data into HDM
+ * @param type type of Initialization data to load @link load_opt Load Host
+ * Data Option @endlink
+ * @return OK if success or an error code which specify the type of error
+ */
+int requestHDMDownload(u8 type)
+{
+ int ret = ERROR_OP_NOT_ALLOW;
+ int retry = 0;
+
+ pr_info("%s: Requesting HDM download...\n", __func__);
+ while (retry < RETRY_FW_HDM_DOWNLOAD) {
+ ret = writeSysCmd(SYS_CMD_LOAD_DATA, &type, 1);
+ /* send request to load in memory the Compensation Data */
+ if (ret < OK) {
+ pr_err("%s: failed at %d attemp!\n",
+ __func__, retry + 1);
+ retry += 1;
+ } else {
+ pr_info("%s: Request HDM Download FINISHED!\n",
+ __func__);
+ return OK;
+ }
+ }
+
+ ret |= ERROR_REQU_HDM_DOWNLOAD;
+ pr_err("%s: Requesting HDM Download... ERROR %08X\n",
+ __func__, ret);
+
+ return ret;
+}
+
+
+/**
+ * Read HDM Header and check that the type loaded match
+ * with the one previously requested
+ * @param type type of Initialization data requested @link load_opt Load Host
+ * Data Option @endlink
+ * @param header pointer to DataHeader variable which will contain the header
+ * @param address pointer to a variable which will contain the updated address
+ * to the next data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readHDMHeader(u8 type, DataHeader *header, u64 *address)
+{
+ u64 offset = ADDR_FRAMEBUFFER;
+ u8 data[HDM_DATA_HEADER];
+ int ret;
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, offset, data,
+ HDM_DATA_HEADER, DUMMY_FRAMEBUFFER);
+ if (ret < OK) { /* i2c function have already a retry mechanism */
+ pr_err("%s: error while reading HDM data header ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("Read HDM Data Header done!\n");
+
+ if (data[0] != HEADER_SIGNATURE) {
+ pr_err("%s: The Header Signature was wrong! %02X != %02X ERROR %08X\n",
+ __func__, data[0], HEADER_SIGNATURE,
+ ERROR_WRONG_DATA_SIGN);
+ return ERROR_WRONG_DATA_SIGN;
+ }
+
+
+ if (data[1] != type) {
+ pr_err("%s: Wrong type found! %02X!=%02X ERROR %08X\n",
+ __func__, data[1], type, ERROR_DIFF_DATA_TYPE);
+ return ERROR_DIFF_DATA_TYPE;
+ }
+
+ pr_info("Type = %02X of Compensation data OK!\n", type);
+
+ header->type = type;
+
+ *address = offset + HDM_DATA_HEADER;
+
+ return OK;
+}
+
+
+/**
+ * Read MS Global Initialization data from the buffer such as Cx1
+ * @param address pointer to a variable which contain the address from where
+ * to read the data and will contain the updated address to the next data
+ * @param global pointer to MutualSenseData variable which will contain the MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readMutualSenseGlobalData(u64 *address, MutualSenseData *global)
+{
+ u8 data[COMP_DATA_GLOBAL];
+ int ret;
+
+ pr_info("Address for Global data= %llx\n", *address);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data,
+ COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading info data ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+ pr_info("Global data Read !\n");
+
+ global->header.force_node = data[0];
+ global->header.sense_node = data[1];
+ global->cx1 = data[2];
+ /* all other bytes are reserved atm */
+
+ pr_info("force_len = %d sense_len = %d CX1 = %d\n",
+ global->header.force_node, global->header.sense_node,
+ global->cx1);
+
+ *address += COMP_DATA_GLOBAL;
+ return OK;
+}
+
+
+/**
+ * Read MS Initialization data for each node from the buffer
+ * @param address a variable which contain the address from where to read the
+ * data
+ * @param node pointer to MutualSenseData variable which will contain the MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readMutualSenseNodeData(u64 address, MutualSenseData *node)
+{
+ int ret;
+ int size = node->header.force_node * node->header.sense_node;
+
+ pr_info("Address for Node data = %llx\n", address);
+
+ node->node_data = (i8 *)kmalloc(size * (sizeof(i8)), GFP_KERNEL);
+
+ if (node->node_data == NULL) {
+ pr_err("%s: can not allocate node_data... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+
+ pr_info("Node Data to read %d bytes\n", size);
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address,
+ node->node_data, size, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading node data ERROR %08X\n",
+ __func__, ret);
+ kfree(node->node_data);
+ return ret;
+ }
+ node->node_data_size = size;
+
+ pr_info("Read node data OK!\n");
+
+ return size;
+}
+
+/**
+ * Perform all the steps to read the necessary info for MS Initialization data
+ * from the buffer and store it in a MutualSenseData variable
+ * @param type type of MS Initialization data to read @link load_opt Load Host
+ * Data Option @endlink
+ * @param data pointer to MutualSenseData variable which will contain the MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readMutualSenseCompensationData(u8 type, MutualSenseData *data)
+{
+ int ret;
+ u64 address;
+
+ data->node_data = NULL;
+
+ if (!(type == LOAD_CX_MS_TOUCH || type == LOAD_CX_MS_LOW_POWER ||
+ type == LOAD_CX_MS_KEY || type == LOAD_CX_MS_FORCE)) {
+ pr_err("%s: Choose a MS type of compensation data ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ ret = requestHDMDownload(type);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ret);
+ return ret;
+ }
+
+ ret = readHDMHeader(type, &(data->header), &address);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_HDM_DATA_HEADER);
+ return ret | ERROR_HDM_DATA_HEADER;
+ }
+
+ ret = readMutualSenseGlobalData(&address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_GLOBAL);
+ return ret | ERROR_COMP_DATA_GLOBAL;
+ }
+
+ ret = readMutualSenseNodeData(address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_NODE);
+ return ret | ERROR_COMP_DATA_NODE;
+ }
+
+ return OK;
+}
+
+/**
+ * Read SS Global Initialization data from the buffer such as Ix1/Cx1 for force
+ * and sense
+ * @param address pointer to a variable which contain the address from where
+ * to read the data and will contain the updated address to the next data
+ * @param global pointer to MutualSenseData variable which will contain the SS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readSelfSenseGlobalData(u64 *address, SelfSenseData *global)
+{
+ int ret;
+ u8 data[COMP_DATA_GLOBAL];
+
+ pr_info("Address for Global data= %llx\n", *address);
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data,
+ COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading the data... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("Global data Read !\n");
+
+
+ global->header.force_node = data[0];
+ global->header.sense_node = data[1];
+ global->f_ix1 = data[2];
+ global->s_ix1 = data[3];
+ global->f_cx1 = (i8)data[4];
+ global->s_cx1 = (i8)data[5];
+ global->f_max_n = data[6];
+ global->s_max_n = data[7];
+ global->f_ix0 = data[8];
+ global->s_ix0 = data[9];
+
+ pr_info("force_len = %d sense_len = %d f_ix1 = %d s_ix1 = %d f_cx1 = %d s_cx1 = %d\n",
+ global->header.force_node, global->header.sense_node,
+ global->f_ix1, global->s_ix1, global->f_cx1, global->s_cx1);
+ pr_info("max_n = %d s_max_n = %d f_ix0 = %d s_ix0 = %d\n",
+ global->f_max_n, global->s_max_n, global->f_ix0,
+ global->s_ix0);
+
+
+ *address += COMP_DATA_GLOBAL;
+
+ return OK;
+}
+
+/**
+ * Read SS Initialization data for each node of force and sense channels from
+ * the buffer
+ * @param address a variable which contain the address from where to read the
+ * data
+ * @param node pointer to SelfSenseData variable which will contain the SS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readSelfSenseNodeData(u64 address, SelfSenseData *node)
+{
+ int size = node->header.force_node * 2 + node->header.sense_node * 2;
+ u8 *data;
+ int ret;
+
+ if (size <= 0) {
+ pr_err("%s: Invalid SS data length!\n", __func__);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ data = kzalloc(size * sizeof(u8), GFP_KERNEL);
+ if (data == NULL)
+ return ERROR_ALLOC;
+
+ node->ix2_fm = (u8 *)kmalloc(node->header.force_node * (sizeof(u8)),
+ GFP_KERNEL);
+ if (node->ix2_fm == NULL) {
+ pr_err("%s: can not allocate memory for ix2_fm... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+ node->cx2_fm = (i8 *)kmalloc(node->header.force_node * (sizeof(i8)),
+ GFP_KERNEL);
+ if (node->cx2_fm == NULL) {
+ pr_err("%s: can not allocate memory for cx2_fm ... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix2_fm);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+ node->ix2_sn = (u8 *)kmalloc(node->header.sense_node * (sizeof(u8)),
+ GFP_KERNEL);
+ if (node->ix2_sn == NULL) {
+ pr_err("%s: can not allocate memory for ix2_sn ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix2_fm);
+ kfree(node->cx2_fm);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+ node->cx2_sn = (i8 *)kmalloc(node->header.sense_node * (sizeof(i8)),
+ GFP_KERNEL);
+ if (node->cx2_sn == NULL) {
+ pr_err("%s: can not allocate memory for cx2_sn ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix2_fm);
+ kfree(node->cx2_fm);
+ kfree(node->ix2_sn);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+
+ pr_info("Address for Node data = %llx\n", address);
+
+ pr_info("Node Data to read %d bytes\n", size);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data,
+ size, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading data... ERROR %08X\n",
+ __func__, ret);
+ kfree(node->ix2_fm);
+ kfree(node->cx2_fm);
+ kfree(node->ix2_sn);
+ kfree(node->cx2_sn);
+ kfree(data);
+ return ret;
+ }
+
+ pr_info("Read node data ok!\n");
+
+ 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;
+}
+
+/**
+ * Perform all the steps to read the necessary info for SS Initialization data
+ * from the buffer and store it in a SelfSenseData variable
+ * @param type type of SS Initialization data to read @link load_opt Load Host
+ * Data Option @endlink
+ * @param data pointer to SelfSenseData variable which will contain the SS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readSelfSenseCompensationData(u8 type, SelfSenseData *data)
+{
+ int ret;
+ u64 address;
+
+ data->ix2_fm = NULL;
+ data->cx2_fm = NULL;
+ data->ix2_sn = NULL;
+ data->cx2_sn = NULL;
+
+ if (!(type == LOAD_CX_SS_TOUCH || type == LOAD_CX_SS_TOUCH_IDLE ||
+ type == LOAD_CX_SS_KEY || type == LOAD_CX_SS_FORCE)) {
+ pr_err("%s: Choose a SS type of compensation data ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ ret = requestHDMDownload(type);
+ if (ret < 0) {
+ pr_err("%s: error while requesting data... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = readHDMHeader(type, &(data->header), &address);
+ if (ret < 0) {
+ pr_err("%s: error while reading data header... ERROR %08X\n",
+ __func__, ERROR_HDM_DATA_HEADER);
+ return ret | ERROR_HDM_DATA_HEADER;
+ }
+
+ ret = readSelfSenseGlobalData(&address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_GLOBAL);
+ return ret | ERROR_COMP_DATA_GLOBAL;
+ }
+
+ ret = readSelfSenseNodeData(address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_NODE);
+ return ret | ERROR_COMP_DATA_NODE;
+ }
+
+ return OK;
+}
+
+/**
+ * Read TOT MS Global Initialization data from the buffer such as number of
+ * force and sense channels
+ * @param address pointer to a variable which contain the address from where
+ * to read the data and will contain the updated address to the next data
+ * @param global pointer to a variable which will contain the TOT MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readTotMutualSenseGlobalData(u64 *address, TotMutualSenseData *global)
+{
+ int ret;
+ u8 data[COMP_DATA_GLOBAL];
+
+ pr_info("Address for Global data= %llx\n", *address);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data,
+ COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading info data ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+ pr_info("Global data Read !\n");
+
+ global->header.force_node = data[0];
+ global->header.sense_node = data[1];
+ /* all other bytes are reserved atm */
+
+ pr_info("force_len = %d sense_len = %d\n",
+ global->header.force_node, global->header.sense_node);
+
+ *address += COMP_DATA_GLOBAL;
+ return OK;
+}
+
+
+/**
+ * Read TOT MS Initialization data for each node from the buffer
+ * @param address a variable which contain the address from where to read the
+ * data
+ * @param node pointer to MutualSenseData variable which will contain the TOT
+ * MS initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readTotMutualSenseNodeData(u64 address, TotMutualSenseData *node)
+{
+ int ret, i;
+ int size = node->header.force_node * node->header.sense_node;
+ int toRead = size * sizeof(u16);
+ u8 *data;
+
+ if (size <= 0) {
+ pr_err("%s: Invalid MS data length!\n", __func__);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ data = kzalloc(toRead * sizeof(u8), GFP_KERNEL);
+ if (data == NULL)
+ return ERROR_ALLOC;
+
+ pr_info("Address for Node data = %llx\n", address);
+
+ node->node_data = (short *)kmalloc(size * (sizeof(short)), GFP_KERNEL);
+
+ if (node->node_data == NULL) {
+ pr_err("%s: can not allocate node_data... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+ pr_info("Node Data to read %d bytes\n", size);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data,
+ toRead, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading node data ERROR %08X\n",
+ __func__, ret);
+ kfree(node->node_data);
+ kfree(data);
+ return ret;
+ }
+ node->node_data_size = size;
+
+ for (i = 0; i < size; i++)
+ node->node_data[i] = ((short)data[i * 2 + 1]) << 8 |
+ data[i * 2];
+
+ pr_info("Read node data OK!\n");
+
+ kfree(data);
+ return size;
+}
+
+/**
+ * Perform all the steps to read the necessary info for TOT MS Initialization
+ * data from the buffer and store it in a TotMutualSenseData variable
+ * @param type type of TOT MS Initialization data to read @link load_opt Load
+ * Host Data Option @endlink
+ * @param data pointer to a variable which will contain the TOT MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readTotMutualSenseCompensationData(u8 type, TotMutualSenseData *data)
+{
+ int ret;
+ u64 address;
+
+ data->node_data = NULL;
+
+ if (!(type == LOAD_PANEL_CX_TOT_MS_TOUCH || type ==
+ LOAD_PANEL_CX_TOT_MS_LOW_POWER ||
+ type == LOAD_PANEL_CX_TOT_MS_KEY ||
+ type == LOAD_PANEL_CX_TOT_MS_FORCE)) {
+ pr_err("%s: Choose a TOT MS type of compensation data ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ ret = requestHDMDownload(type);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ret);
+ return ret;
+ }
+
+ ret = readHDMHeader(type, &(data->header), &address);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_HDM_DATA_HEADER);
+ return ret | ERROR_HDM_DATA_HEADER;
+ }
+
+ ret = readTotMutualSenseGlobalData(&address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_GLOBAL);
+ return ret | ERROR_COMP_DATA_GLOBAL;
+ }
+
+ ret = readTotMutualSenseNodeData(address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_NODE);
+ return ret | ERROR_COMP_DATA_NODE;
+ }
+
+ return OK;
+}
+
+/**
+ * Read TOT SS Global Initialization data from the buffer such as number of
+ * force and sense channels
+ * @param address pointer to a variable which contain the address from where
+ * to read the data and will contain the updated address to the next data
+ * @param global pointer to a variable which will contain the TOT SS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readTotSelfSenseGlobalData(u64 *address, TotSelfSenseData *global)
+{
+ int ret;
+ u8 data[COMP_DATA_GLOBAL];
+
+ pr_info("Address for Global data= %llx\n", *address);
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data,
+ COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading the data... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("Global data Read !\n");
+
+
+ global->header.force_node = data[0];
+ global->header.sense_node = data[1];
+
+
+ pr_info("force_len = %d sense_len = %d\n",
+ global->header.force_node, global->header.sense_node);
+
+
+ *address += COMP_DATA_GLOBAL;
+
+ return OK;
+}
+
+/**
+ * Read TOT SS Global Initialization data from the buffer such as number of
+ * force and sense channels
+ * @param address pointer to a variable which contain the address from where
+ * to read the data and will contain the updated address to the next data
+ * @param node pointer to a variable which will contain the TOT SS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readTotSelfSenseNodeData(u64 address, TotSelfSenseData *node)
+{
+ int size = node->header.force_node * 2 + node->header.sense_node * 2;
+ int toRead = size * 2; /* *2 2 bytes each node */
+ u8 *data;
+ int ret, i, j = 0;
+
+ if (size <= 0) {
+ pr_err("%s: Invalid Tot SS data length!\n", __func__);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ data = kzalloc(toRead * sizeof(u8), GFP_KERNEL);
+ if (data == NULL)
+ return ERROR_ALLOC;
+
+ node->ix_fm = (u16 *)kmalloc(node->header.force_node * (sizeof(u16)),
+ GFP_KERNEL);
+ if (node->ix_fm == NULL) {
+ pr_err("%s: can not allocate memory for ix2_fm... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+ node->cx_fm = (short *)kmalloc(node->header.force_node *
+ (sizeof(short)), GFP_KERNEL);
+ if (node->cx_fm == NULL) {
+ pr_err("%s: can not allocate memory for cx2_fm ... ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix_fm);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+ node->ix_sn = (u16 *)kmalloc(node->header.sense_node * (sizeof(u16)),
+ GFP_KERNEL);
+ if (node->ix_sn == NULL) {
+ pr_err("%s: can not allocate memory for ix2_sn ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix_fm);
+ kfree(node->cx_fm);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+ node->cx_sn = (short *)kmalloc(node->header.sense_node *
+ (sizeof(short)), GFP_KERNEL);
+ if (node->cx_sn == NULL) {
+ pr_err("%s: can not allocate memory for cx2_sn ERROR %08X",
+ __func__, ERROR_ALLOC);
+ kfree(node->ix_fm);
+ kfree(node->cx_fm);
+ kfree(node->ix_sn);
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+
+ pr_info("Address for Node data = %llx\n", address);
+
+ pr_info("Node Data to read %d bytes\n", size);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data,
+ toRead, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading data... ERROR %08X\n",
+ __func__, ret);
+ kfree(node->ix_fm);
+ kfree(node->cx_fm);
+ kfree(node->ix_sn);
+ kfree(node->cx_sn);
+ kfree(data);
+ return ret;
+ }
+
+ pr_info("Read node data ok!\n");
+
+ j = 0;
+ for (i = 0; i < node->header.force_node; i++) {
+ node->ix_fm[i] = ((u16)data[j + 1]) << 8 | data[j];
+ j += 2;
+ }
+
+ for (i = 0; i < node->header.sense_node; i++) {
+ node->ix_sn[i] = ((u16)data[j + 1]) << 8 | data[j];
+ j += 2;
+ }
+
+ for (i = 0; i < node->header.force_node; i++) {
+ node->cx_fm[i] = ((short)data[j + 1]) << 8 | data[j];
+ j += 2;
+ }
+
+ for (i = 0; i < node->header.sense_node; i++) {
+ node->cx_sn[i] = ((short)data[j + 1]) << 8 | data[j];
+ j += 2;
+ }
+
+ if (j != toRead)
+ pr_err("%s: parsed a wrong number of bytes %d!=%d\n",
+ __func__, j, toRead);
+
+ kfree(data);
+ return OK;
+}
+
+/**
+ * Perform all the steps to read the necessary info for TOT SS Initialization
+ * data from the buffer and store it in a TotSelfSenseData variable
+ * @param type type of TOT MS Initialization data to read @link load_opt Load
+ * Host Data Option @endlink
+ * @param data pointer to a variable which will contain the TOT MS
+ * initialization data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readTotSelfSenseCompensationData(u8 type, TotSelfSenseData *data)
+{
+ int ret;
+ u64 address;
+
+ data->ix_fm = NULL;
+ data->cx_fm = NULL;
+ data->ix_sn = NULL;
+ data->cx_sn = NULL;
+
+ if (!(type == LOAD_PANEL_CX_TOT_SS_TOUCH || type ==
+ LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE || type ==
+ LOAD_PANEL_CX_TOT_SS_KEY ||
+ type == LOAD_PANEL_CX_TOT_SS_FORCE)) {
+ pr_err("%s: Choose a TOT SS type of compensation data ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ ret = requestHDMDownload(type);
+ if (ret < 0) {
+ pr_err("%s: error while requesting data... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = readHDMHeader(type, &(data->header), &address);
+ if (ret < 0) {
+ pr_err("%s: error while reading data header... ERROR %08X\n",
+ __func__, ERROR_HDM_DATA_HEADER);
+ return ret | ERROR_HDM_DATA_HEADER;
+ }
+
+ ret = readTotSelfSenseGlobalData(&address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_GLOBAL);
+ return ret | ERROR_COMP_DATA_GLOBAL;
+ }
+
+ ret = readTotSelfSenseNodeData(address, data);
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_NODE);
+ return ret | ERROR_COMP_DATA_NODE;
+ }
+
+ return OK;
+}
+
+
+/**
+ * Read Initialization Data Header for the Coefficients and check that the type
+ * loaded match with the one previously requested
+ * @param type type of Coefficients data requested @link load_opt Load Host
+ * Data Option @endlink
+ * @param msHeader pointer to DataHeader variable for the MS Coefficients
+ * @param ssHeader pointer to DataHeader variable for the SS Coefficients
+ * @param address pointer to a variable which will contain the updated address
+ * to the next data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readSensitivityCoeffHeader(u8 type, DataHeader *msHeader,
+ DataHeader *ssHeader, u64 *address)
+{
+ u64 offset = ADDR_FRAMEBUFFER;
+ u8 data[SYNCFRAME_DATA_HEADER];
+ int ret;
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, offset, data,
+ SYNCFRAME_DATA_HEADER, DUMMY_FRAMEBUFFER);
+ if (ret < OK) { /* i2c function have already a retry mechanism */
+ pr_err("%s: error while reading data header ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("Read Data Header done!\n");
+
+ if (data[0] != HEADER_SIGNATURE) {
+ pr_err("%s: The Header Signature was wrong! %02X != %02X ERROR %08X\n",
+ __func__, data[0], HEADER_SIGNATURE,
+ ERROR_WRONG_DATA_SIGN);
+ return ERROR_WRONG_DATA_SIGN;
+ }
+
+
+ if (data[1] != type) {
+ pr_err("%s: Wrong type found! %02X!=%02X ERROR %08X\n",
+ __func__, data[1], type, ERROR_DIFF_DATA_TYPE);
+ return ERROR_DIFF_DATA_TYPE;
+ }
+
+ pr_info("Type = %02X of Compensation data OK!\n", type);
+
+ msHeader->type = type;
+ ssHeader->type = type;
+
+ msHeader->force_node = data[5];
+ msHeader->sense_node = data[6];
+ pr_info("MS Force Len = %d Sense Len = %d\n",
+ msHeader->force_node, msHeader->sense_node);
+
+ ssHeader->force_node = data[7];
+ ssHeader->sense_node = data[8];
+ pr_info("SS Force Len = %d Sense Len = %d\n",
+ ssHeader->force_node, ssHeader->sense_node);
+
+ *address = offset + SYNCFRAME_DATA_HEADER;
+
+ return OK;
+}
+
+
+/**
+ * Read MS and SS Sensitivity Coefficients for from the IC
+ * @param address a variable which contain the address from where to read the
+ * data
+ * @param msCoeff pointer to MutualSenseCoeff variable which will contain the
+ * MS Coefficient data
+ * @param ssCoeff pointer to SelfSenseCoeff variable which will contain the SS
+ * Coefficient data
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readSensitivityCoeffNodeData(u64 address, MutualSenseCoeff *msCoeff,
+ SelfSenseCoeff *ssCoeff)
+{
+ int size = msCoeff->header.force_node * msCoeff->header.sense_node +
+ (ssCoeff->header.force_node + ssCoeff->header.sense_node);
+ u8 *data;
+ int ret;
+
+ if (size <= 0) {
+ pr_err("%s:Invalid SS coeff. length!\n", __func__);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ data = kzalloc(size * sizeof(u8), GFP_KERNEL);
+ if (data == NULL)
+ return ERROR_ALLOC;
+
+ msCoeff->node_data_size = msCoeff->header.force_node *
+ msCoeff->header.sense_node;
+
+ msCoeff->ms_coeff = (u8 *)kmalloc(msCoeff->node_data_size *
+ (sizeof(u8)), GFP_KERNEL);
+
+ ssCoeff->ss_force_coeff = (u8 *)kmalloc(ssCoeff->header.force_node *
+ (sizeof(u8)), GFP_KERNEL);
+
+ ssCoeff->ss_sense_coeff = (u8 *)kmalloc(ssCoeff->header.sense_node *
+ (sizeof(u8)), GFP_KERNEL);
+ if (msCoeff->ms_coeff == NULL ||
+ ssCoeff->ss_force_coeff == NULL ||
+ ssCoeff->ss_sense_coeff == NULL) {
+
+ pr_err("%s: can not allocate memory for coeff ERROR %08X",
+ __func__, ERROR_ALLOC);
+
+ kfree(msCoeff->ms_coeff);
+ msCoeff->ms_coeff = NULL;
+
+ kfree(ssCoeff->ss_force_coeff);
+ ssCoeff->ss_force_coeff = NULL;
+
+ kfree(ssCoeff->ss_sense_coeff);
+ ssCoeff->ss_sense_coeff = NULL;
+
+ kfree(data);
+ return ERROR_ALLOC;
+ }
+
+ pr_info("Address for Node data = %llx\n", address);
+
+ pr_info("Node Data to read %d bytes\n", size);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data,
+ size, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading data... ERROR %08X\n",
+ __func__, ret);
+ kfree(msCoeff->ms_coeff);
+ msCoeff->ms_coeff = NULL;
+ kfree(ssCoeff->ss_force_coeff);
+ ssCoeff->ss_force_coeff = NULL;
+ kfree(ssCoeff->ss_sense_coeff);
+ ssCoeff->ss_sense_coeff = NULL;
+ kfree(data);
+ return ret;
+ }
+
+ pr_info("Read node data ok!\n");
+
+ memcpy(msCoeff->ms_coeff, data, msCoeff->node_data_size);
+ memcpy(ssCoeff->ss_force_coeff, &data[msCoeff->node_data_size],
+ ssCoeff->header.force_node);
+ memcpy(ssCoeff->ss_sense_coeff, &data[msCoeff->node_data_size +
+ ssCoeff->header.force_node],
+ ssCoeff->header.sense_node);
+
+ kfree(data);
+ return OK;
+}
+
+
+/**
+ * Perform all the steps to read Sensitivity Coefficients and store into the
+ * corresponding variables
+ * @param msCoeff pointer to a variable which will contain the MS Sensitivity
+ * Coefficients
+ * @param ssCoeff pointer to a variable which will contain the SS Sensitivity
+ * Coefficients
+ * @return OK if success or an error code which specify the type of error
+ */
+int readSensitivityCoefficientsData(MutualSenseCoeff *msCoeff,
+ SelfSenseCoeff *ssCoeff)
+{
+ int ret;
+ u64 address;
+
+ msCoeff->ms_coeff = NULL;
+ ssCoeff->ss_force_coeff = NULL;
+ ssCoeff->ss_sense_coeff = NULL;
+
+
+ ret = requestHDMDownload(LOAD_SENS_CAL_COEFF);
+ if (ret < OK) {
+ pr_err("%s: error while requesting data... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = readSensitivityCoeffHeader(LOAD_SENS_CAL_COEFF,
+ &(msCoeff->header), &(ssCoeff->header),
+ &address);
+ if (ret < OK) {
+ pr_err("%s: error while reading data header... ERROR %08X\n",
+ __func__, ERROR_HDM_DATA_HEADER);
+ return ret | ERROR_HDM_DATA_HEADER;
+ }
+
+ ret = readSensitivityCoeffNodeData(address, msCoeff, ssCoeff);
+ if (ret < OK) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_COMP_DATA_NODE);
+ return ret | ERROR_COMP_DATA_NODE;
+ }
+
+ return OK;
+}
+
+/**
+ * Read Golden Mutual Raw data from FW Host Data Memory.
+ * @param address a variable which contain the address from where to read the
+ * data
+ * @param node pointer to GoldenMutualRawData variable.
+ * @return OK if success or an error code which specify the type of error
+ */
+static int readGoldenMutualData(GoldenMutualRawData *pgmData, u64 address)
+{
+ u32 size, i;
+ int ret;
+
+ pgmData->data_size = 0;
+ pgmData->data = NULL;
+
+ pr_info("Address for Golden Mutual hdr = %llx\n", address);
+
+ /* read 12 byte Golden Mutual header */
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16,
+ address, (u8 *)&(pgmData->hdr),
+ GM_DATA_HEADER, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("error while reading Golden Mutual hdr... ERROR %08X\n",
+ ret);
+ goto out;
+ }
+
+ pr_info("ms_force_len = %u ms_sense_len = %u\n",
+ pgmData->hdr.ms_f_len, pgmData->hdr.ms_s_len);
+ pr_info("ss_force_len = %u ss_sense_len = %u\n",
+ pgmData->hdr.ss_f_len, pgmData->hdr.ss_s_len);
+ pr_info("ms_key_len = %u \n", pgmData->hdr.ms_k_len);
+
+ size = pgmData->hdr.ms_f_len * pgmData->hdr.ms_s_len;
+
+ pgmData->data = kzalloc(size * sizeof(s16), GFP_KERNEL);
+ if (pgmData->data == NULL) {
+ ret = ERROR_ALLOC;
+ pr_err("Unable to allocate memory for GM raw data. ERR %08X",
+ ret);
+ goto out;
+ }
+
+ /* go past both HDM and GM header to read the data */
+ address += GM_DATA_HEADER;
+ pr_info("Address for Golden Mutual data = %llx\n", address);
+
+ //read the data buffer.
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address,
+ (u8 *)pgmData->data, size * sizeof(s16),
+ DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("error while reading Golden Mutual data... ERROR %08X\n", ret);
+ kfree(pgmData->data);
+ pgmData->data = NULL;
+ goto out;
+ }
+
+ pgmData->data_size = size;
+
+ pr_info("Read data ok!\n");
+
+ for (i = 0; i < size; i++)
+ le16_to_cpus(&pgmData->data[i]);
+
+ ret = OK;
+out:
+ return ret;
+}
+
+/**
+ * Perform all the steps to read the necessary info for Golden Mutual raw
+ * data from the buffer and store it in a GoldemMutualRawData object.
+ * @param pointer to GoldemMutualRawData variable which will contain
+ * the raw data.
+ * @return OK if success or an error code which specify the type of error
+ */
+int readGoldenMutualRawData(GoldenMutualRawData *pgmData)
+{
+ int ret;
+ u64 address;
+
+ ret = requestHDMDownload(LOAD_GOLDEN_MUTUAL_RAW);
+ if (ret < 0) {
+ pr_err("error while requesting HDM Download... ERROR %08X\n",
+ ret);
+ goto out;
+ }
+
+ ret = readHDMHeader(LOAD_GOLDEN_MUTUAL_RAW,
+ &pgmData->hdm_hdr, &address);
+ if (ret < 0) {
+ pr_err("error reading HDM header... ERROR %08X\n",
+ ERROR_HDM_DATA_HEADER);
+ ret |= ERROR_HDM_DATA_HEADER;
+ goto out;
+ }
+
+ ret = readGoldenMutualData(pgmData, address);
+ if (ret < 0) {
+ pr_err("error reading Golden Mutual data... ERROR %08X\n",
+ ERROR_GOLDEN_MUTUAL_DATA);
+ ret |= ERROR_GOLDEN_MUTUAL_DATA;
+ goto out;
+ }
+
+ ret = OK;
+out:
+ return ret;
+}
diff --git a/fts_lib/ftsCompensation.h b/fts_lib/ftsCompensation.h
new file mode 100644
index 0000000..64c07e0
--- /dev/null
+++ b/fts_lib/ftsCompensation.h
@@ -0,0 +1,181 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS functions for getting Initialization Data **
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsCompensation.h
+ * \brief Contains all the definitions and structs to work with Initialization
+ * Data
+ */
+
+#ifndef FTS_COMPENSATION_H
+#define FTS_COMPENSATION_H
+
+#include "ftsCore.h"
+#include "ftsSoftware.h"
+
+
+
+#define RETRY_FW_HDM_DOWNLOAD 2 /* /< max number of attempts to
+ * request HDM download */
+
+
+/* Bytes dimension of HDM content */
+
+#define HDM_DATA_HEADER DATA_HEADER /* /< size in bytes of
+ * initialization data header */
+#define COMP_DATA_GLOBAL (16 - HDM_DATA_HEADER) /* /< size in bytes
+ * of initialization
+ * data general info */
+#define GM_DATA_HEADER (16 - HDM_DATA_HEADER) /* /< size in bytes of
+ * Golden Mutual data header */
+
+
+#define HEADER_SIGNATURE 0xA5 /* /< signature used as starting byte of
+ * data loaded in memory */
+
+
+
+/**
+ * Struct which contains the general info about Frames and Initialization Data
+ */
+typedef struct {
+ int force_node; /* /< Number of Force Channels in the
+ * frame/Initialization data */
+ int sense_node; /* /< Number of Sense Channels in the
+ * frame/Initialization data */
+ int type; /* /< Type of frame/Initialization data */
+} DataHeader;
+
+/**
+ * Struct which contains the MS Initialization data
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+ i8 cx1; /* /< Cx1 value (can be negative)) */
+ i8 *node_data; /* /< Pointer to an array of bytes which contains the
+ * CX2 data (can be negative) */
+ int node_data_size; /* /< size of the data */
+} MutualSenseData;
+
+
+/**
+ * Struct which contains the SS Initialization data
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+ u8 f_ix1; /* /< IX1 Force */
+ u8 s_ix1; /* /< IX1 Sense */
+ i8 f_cx1; /* /< CX1 Force (can be negative) */
+ i8 s_cx1; /* /< CX1 Sense (can be negative) */
+ u8 f_max_n; /* /< Force MaxN */
+ u8 s_max_n; /* /< Sense MaxN */
+ u8 f_ix0; /* /< IX0 Force */
+ u8 s_ix0; /* /< IX0 Sense */
+
+ u8 *ix2_fm; /* /< pointer to an array of bytes which contains Force
+ * Ix2 data node */
+ u8 *ix2_sn; /* /< pointer to an array of bytes which contains Sense
+ * Ix2 data node */
+ i8 *cx2_fm; /* /< pointer to an array of bytes which contains Force
+ * Cx2 data node
+ * (can be negative) */
+ i8 *cx2_sn; /* /< pointer to an array of bytes which contains Sense
+ * Cx2 data node
+ * (can be negative)) */
+} SelfSenseData;
+
+/**
+ * Struct which contains the TOT MS Initialization data
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+ short *node_data; /* /< pointer to an array of ushort which
+ * contains TOT MS Initialization data */
+ int node_data_size; /* /< size of data */
+} TotMutualSenseData;
+
+/**
+ * Struct which contains the TOT SS Initialization data
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+
+ u16 *ix_fm; /* /< pointer to an array of ushort which contains TOT
+ * SS IX Force data */
+ u16 *ix_sn; /* /< pointer to an array of ushort which contains TOT
+ * SS IX Sense data */
+ short *cx_fm; /* /< pointer to an array of ushort which contains TOT
+ * SS CX Force data
+ * (can be negative) */
+ short *cx_sn; /* /< pointer to an array of ushort which contains TOT
+ * SS CX Sense data
+ * (can be negative) */
+} TotSelfSenseData;
+
+/**
+ * Struct which contains the Mutual Sense Sensitivity Calibration Coefficients
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+ u8 *ms_coeff; /* /< Pointer to an array of bytes which contains the MS
+ * Sens coeff */
+ int node_data_size; /* /< size of coefficients */
+} MutualSenseCoeff;
+
+/**
+ * Struct which contains the Self Sense Sensitivity Calibration Coefficients
+ */
+typedef struct {
+ DataHeader header; /* /< Header */
+ u8 *ss_force_coeff; /* /< Pointer to an array of bytes which
+ * contains the SS Sens Force coeff */
+ u8 *ss_sense_coeff; /* /< Pointer to an array of bytes which
+ * contains the SS Sens Sense coeff */
+} SelfSenseCoeff;
+
+/**
+ * Struct which contains the Golden Mutual Header
+ */
+typedef struct {
+ u8 ms_f_len; /* /< ms force length */
+ u8 ms_s_len; /* /< ms sense length */
+ u8 ss_f_len; /* /< ss force length */
+ u8 ss_s_len; /* /< ss sense length */
+ u8 ms_k_len; /* /< ms key length */
+ u8 reserved_0[3];
+ u32 reserved_1;
+}GoldenMutualHdr;
+
+/**
+ * Struct which contains the Golden Mutual Raw data
+ */
+typedef struct {
+ DataHeader hdm_hdr; /* /< HDM Header */
+ GoldenMutualHdr hdr; /* /< Golden Mutual Data Hdr */
+ s16 *data; /* /< pointer to the raw data */
+ u32 data_size; /* /< size of raw data buffer */
+} GoldenMutualRawData;
+
+int requestHDMDownload(u8 type);
+int readHDMHeader(u8 type, DataHeader *header, u64 *address);
+int readMutualSenseCompensationData(u8 type, MutualSenseData *data);
+int readSelfSenseCompensationData(u8 type, SelfSenseData *data);
+int readTotMutualSenseCompensationData(u8 type, TotMutualSenseData *data);
+int readTotSelfSenseCompensationData(u8 type, TotSelfSenseData *data);
+int readSensitivityCoefficientsData(MutualSenseCoeff *msData,
+ SelfSenseCoeff *ssData);
+int readGoldenMutualRawData(GoldenMutualRawData *pgmData);
+
+#endif
diff --git a/fts_lib/ftsCore.c b/fts_lib/ftsCore.c
new file mode 100644
index 0000000..b073806
--- /dev/null
+++ b/fts_lib/ftsCore.c
@@ -0,0 +1,1243 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Core functions *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsCore.c
+ * \brief Contains the implementation of the Core functions
+ */
+
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include "ftsCompensation.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsIO.h"
+#include "ftsTest.h"
+#include "ftsTime.h"
+#include "ftsTool.h"
+#include "ftsFrame.h"
+
+/** @addtogroup system_info
+ * @{
+ */
+SysInfo systemInfo; /* Global System Info variable, accessible in all the
+ * driver */
+/** @}*/
+
+static int reset_gpio = GPIO_NOT_DEFINED;/* /< gpio number of the rest pin,
+ * the value is GPIO_NOT_DEFINED
+ * if the reset pin is not connected */
+static int system_reseted_up; /* /< flag checked during resume to understand
+ * if there was a system reset
+ * and restore the proper state */
+static int system_reseted_down; /* /< flag checked during suspend to understand
+ * if there was a system reset
+ * and restore the proper state */
+
+/**
+ * Initialize core variables of the library.
+ * Must be called during the probe before any other lib function
+ * @param info pointer to fts_ts_info which contains info about the device and
+ * its hw setup
+ * @return OK if success or an error code which specify the type of error
+ */
+int initCore(struct fts_ts_info *info)
+{
+ int ret = OK;
+
+ pr_info("%s: Initialization of the Core...\n", __func__);
+ ret |= openChannel(info->client);
+ ret |= resetErrorList();
+ ret |= initTestToDo();
+ setResetGpio(info->board->reset_gpio);
+ if (ret < OK)
+ pr_err("%s: Initialization Core ERROR %08X!\n",
+ __func__, ret);
+ else
+ pr_info("%s: Initialization Finished!\n",
+ __func__);
+ return ret;
+}
+
+/**
+ * Set the reset_gpio variable with the actual gpio number of the board link to
+ * the reset pin
+ * @param gpio gpio number link to the reset pin of the IC
+ */
+void setResetGpio(int gpio)
+{
+ reset_gpio = gpio;
+ pr_info("setResetGpio: reset_gpio = %d\n", reset_gpio);
+}
+
+/**
+ * Perform a system reset of the IC.
+ * If the reset pin is associated to a gpio, the function execute an hw reset
+ * (toggling of reset pin) otherwise send an hw command to the IC
+ * @return OK if success or an error code which specify the type of error
+ */
+int fts_system_reset(void)
+{
+ u8 readData[FIFO_EVENT_SIZE];
+ int event_to_search;
+ int res = -1;
+ int i;
+ u8 data[1] = { SYSTEM_RESET_VALUE };
+#ifdef SUPPORT_PROX_PALM
+ struct fts_ts_info *info = NULL;
+ info = dev_get_drvdata(&getClient()->dev);
+#endif
+
+ event_to_search = (int)EVT_ID_CONTROLLER_READY;
+
+ pr_info("System resetting...\n");
+ for (i = 0; i < RETRY_SYSTEM_RESET && res < 0; i++) {
+ resetErrorList();
+ fts_enableInterrupt(false);
+ /* disable interrupt before resetting to be able to get boot
+ * events */
+
+ if (reset_gpio == GPIO_NOT_DEFINED)
+ res = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_SYSTEM_RESET, data, ARRAY_SIZE(
+ data));
+ else {
+ gpio_set_value(reset_gpio, 0);
+ msleep(10);
+ gpio_set_value(reset_gpio, 1);
+ res = OK;
+ }
+ if (res < OK)
+ pr_err("fts_system_reset: ERROR %08X\n", ERROR_BUS_W);
+ else {
+ res = pollForEvent(&event_to_search, 1, readData,
+ GENERAL_TIMEOUT);
+ if (res < OK)
+ pr_err("fts_system_reset: ERROR %08X\n", res);
+ }
+ }
+#ifdef SUPPORT_PROX_PALM
+ info->prox_palm_status = 0;
+#endif
+ if (res < OK) {
+ pr_err("fts_system_reset...failed after 3 attempts: ERROR %08X\n",
+ (res | ERROR_SYSTEM_RESET_FAIL));
+ return res | ERROR_SYSTEM_RESET_FAIL;
+ } else {
+ pr_debug("System reset DONE!\n");
+ system_reseted_down = 1;
+ system_reseted_up = 1;
+ return OK;
+ }
+}
+
+/**
+ * Return the value of system_resetted_down.
+ * @return the flag value: 0 if not set, 1 if set
+ */
+int isSystemResettedDown(void)
+{
+ return system_reseted_down;
+}
+
+/**
+ * Return the value of system_resetted_up.
+ * @return the flag value: 0 if not set, 1 if set
+ */
+int isSystemResettedUp(void)
+{
+ return system_reseted_up;
+}
+
+
+/**
+ * Set the value of system_reseted_down flag
+ * @param val value to write in the flag
+ */
+void setSystemResetedDown(int val)
+{
+ system_reseted_down = val;
+}
+
+/**
+ * Set the value of system_reseted_up flag
+ * @param val value to write in the flag
+ */
+void setSystemResetedUp(int val)
+{
+ system_reseted_up = val;
+}
+
+
+/** @addtogroup events_group
+ * @{
+ */
+
+/**
+ * Poll the FIFO looking for a specified event within a timeout. Support a
+ * retry mechanism.
+ * @param event_to_search pointer to an array of int where each element
+ * correspond to a byte of the event to find.
+ * If the element of the array has value -1, the byte of the event,
+ * in the same position of the element is ignored.
+ * @param event_bytes size of event_to_search
+ * @param readData pointer to an array of byte which will contain the event
+ * found
+ * @param time_to_wait time to wait before going in timeout
+ * @return OK if success or an error code which specify the type of error
+ */
+int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int
+ time_to_wait)
+{
+ const u8 NO_RESPONSE = 0xFF;
+ const int POLL_SLEEP_TIME_MS = 5;
+ int i, find, retry, count_err;
+ int time_to_count;
+ int err_handling = OK;
+ StopWatch clock;
+
+ u8 cmd[1] = { FIFO_CMD_READONE };
+ char temp[128] = { 0 };
+
+ find = 0;
+ retry = 0;
+ count_err = 0;
+ time_to_count = time_to_wait / POLL_SLEEP_TIME_MS;
+
+ startStopWatch(&clock);
+ while (find != 1 && retry < time_to_count &&
+ fts_writeReadU8UX(cmd[0], 0, 0, readData, FIFO_EVENT_SIZE,
+ DUMMY_FIFO)
+ >= OK) {
+ if (readData[0] == NO_RESPONSE ||
+ readData[0] == EVT_ID_NOEVENT) {
+ /* No events available, so sleep briefly */
+ msleep(POLL_SLEEP_TIME_MS);
+ retry++;
+ continue;
+ } else if (readData[0] == EVT_ID_ERROR) {
+ /* Log of errors */
+ pr_err("%s\n",
+ printHex("ERROR EVENT = ",
+ readData,
+ FIFO_EVENT_SIZE,
+ temp,
+ sizeof(temp)));
+ memset(temp, 0, 128);
+ count_err++;
+ err_handling = errorHandler(readData, FIFO_EVENT_SIZE);
+ if ((err_handling & 0xF0FF0000) ==
+ ERROR_HANDLER_STOP_PROC) {
+ pr_err("pollForEvent: forced to be stopped! ERROR %08X\n",
+ err_handling);
+ return err_handling;
+ }
+ } else {
+ pr_info("%s\n",
+ printHex("READ EVENT = ", readData,
+ FIFO_EVENT_SIZE,
+ temp,
+ sizeof(temp)));
+ memset(temp, 0, 128);
+
+ if (readData[0] == EVT_ID_CONTROLLER_READY &&
+ event_to_search[0] != EVT_ID_CONTROLLER_READY) {
+ pr_err("pollForEvent: Unmanned Controller Ready Event! Setting reset flags...\n");
+ setSystemResetedUp(1);
+ setSystemResetedDown(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;
+ }
+ }
+ }
+ stopStopWatch(&clock);
+ if ((retry >= time_to_count) && find != 1) {
+ pr_err("pollForEvent: ERROR %08X\n", ERROR_TIMEOUT);
+ return ERROR_TIMEOUT;
+ } else if (find == 1) {
+ pr_info("%s\n",
+ printHex("FOUND EVENT = ",
+ readData,
+ FIFO_EVENT_SIZE,
+ temp,
+ sizeof(temp)));
+ memset(temp, 0, 128);
+ pr_debug("Event found in %d ms (%d iterations)! Number of errors found = %d\n",
+ elapsedMillisecond(&clock), retry, count_err);
+ return count_err;
+ } else {
+ pr_err("pollForEvent: ERROR %08X\n", ERROR_BUS_R);
+ return ERROR_BUS_R;
+ }
+}
+
+/** @}*/
+
+/**
+ * Check that the FW sent the echo even after a command was sent
+ * @param cmd pointer to an array of byte which contain the command previously
+ * sent
+ * @param size size of cmd
+ * @return OK if success or an error code which specify the type of error
+ */
+int checkEcho(u8 *cmd, int size)
+{
+ int ret, i;
+ int event_to_search[FIFO_EVENT_SIZE];
+ u8 readData[FIFO_EVENT_SIZE];
+
+
+ if (size < 1) {
+ pr_err("checkEcho: Error Size = %d not valid!\n", size);
+ return ERROR_OP_NOT_ALLOW;
+ } else {
+ if ((size + 4) > FIFO_EVENT_SIZE)
+ size = FIFO_EVENT_SIZE - 4;
+ /* Echo event 0x43 0x01 xx xx xx xx xx fifo_status
+ * therefore command with more than 4 bytes will be trunked */
+
+ event_to_search[0] = EVT_ID_STATUS_UPDATE;
+ event_to_search[1] = EVT_TYPE_STATUS_ECHO;
+ for (i = 2; i < size + 2; i++)
+ event_to_search[i] = cmd[i - 2];
+ if ((cmd[0] == FTS_CMD_SYSTEM) &&
+ (cmd[1] == SYS_CMD_SPECIAL) &&
+ ((cmd[2] == SPECIAL_FULL_PANEL_INIT) ||
+ (cmd[2] == SPECIAL_PANEL_INIT)))
+ ret = pollForEvent(event_to_search, size + 2, readData,
+ TIMEOUT_ECHO_FPI);
+ else if ((cmd[0] == FTS_CMD_SYSTEM) &&
+ (cmd[1] == SYS_CMD_CX_TUNING))
+ ret = pollForEvent(event_to_search, size + 2, readData,
+ TIMEOUT_ECHO_SINGLE_ENDED_SPECIAL_AUTOTUNE);
+ else if (cmd[0] == FTS_CMD_SYSTEM &&
+ cmd[1] == SYS_CMD_SPECIAL &&
+ cmd[2] == SPECIAL_FIFO_FLUSH)
+ ret = pollForEvent(event_to_search, size + 2, readData,
+ TIMEOUT_ECHO_FLUSH);
+ else
+ ret = pollForEvent(event_to_search, size + 2, readData,
+ TIEMOUT_ECHO);
+ if (ret < OK) {
+ pr_err("checkEcho: Echo Event not found! ERROR %08X\n",
+ ret);
+ return ret | ERROR_CHECK_ECHO_FAIL;
+ } else if (ret > OK) {
+ pr_err("checkEcho: Echo Event found but with some error events before! num_error = %d\n",
+ ret);
+ return ERROR_CHECK_ECHO_FAIL;
+ }
+
+ pr_info("ECHO OK!\n");
+ return ret;
+ }
+}
+
+
+/** @addtogroup scan_mode
+ * @{
+ */
+/**
+ * Set a scan mode in the IC
+ * @param mode scan mode to set; possible values @link scan_opt Scan Mode
+ * Option @endlink
+ * @param settings option for the selected scan mode
+ * (for example @link active_bitmask Active Mode Bitmask @endlink)
+ * @return OK if success or an error code which specify the type of error
+ */
+int setScanMode(u8 mode, u8 settings)
+{
+ u8 cmd[3] = { FTS_CMD_SCAN_MODE, mode, settings };
+ u8 cmd1[7] = {0xFA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int ret, size = 3;
+
+ pr_debug("%s: Setting scan mode: mode = %02X settings = %02X !\n",
+ __func__, mode, settings);
+ if (mode == SCAN_MODE_LOW_POWER)
+ size = 2;
+ ret = fts_write(cmd1, 7);
+ if(ret >= OK)
+ ret = fts_write(cmd, size);
+ /* use write instead of writeFw because can be called while the
+ * interrupt are enabled */
+ if (ret < OK) {
+ pr_err("%s: write failed...ERROR %08X !\n",
+ __func__, ret);
+ return ret | ERROR_SET_SCAN_MODE_FAIL;
+ }
+ pr_debug("%s: Setting scan mode OK!\n", __func__);
+ return OK;
+}
+/** @}*/
+
+
+/** @addtogroup feat_sel
+ * @{
+ */
+/**
+ * Set a feature and its option in the IC
+ * @param feat feature to set; possible values @link feat_opt Feature Selection
+ * Option @endlink
+ * @param settings pointer to an array of byte which store the options for
+ * the selected feature (for example the gesture mask to activate
+ * @link gesture_opt Gesture IDs @endlink)
+ * @param size in bytes of settings
+ * @return OK if success or an error code which specify the type of error
+ */
+int setFeatures(u8 feat, u8 *settings, int size)
+{
+ u8 *cmd;
+ int i = 0;
+ int ret;
+ char *buff;
+ int buff_len = ((2 + 1) * size + 1) * sizeof(char);
+ int index = 0;
+ u8 cmd1[7] = {0xFA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ cmd = kzalloc((2 + size) * sizeof(u8), GFP_KERNEL);
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if ((buff == NULL) || (cmd == NULL)) {
+ kfree(buff);
+ kfree(cmd);
+ return ERROR_ALLOC;
+ }
+
+ pr_info("%s: Setting feature: feat = %02X !\n", __func__, feat);
+ cmd[0] = FTS_CMD_FEATURE;
+ cmd[1] = feat;
+ for (i = 0; i < size; i++) {
+ cmd[2 + i] = settings[i];
+ index += scnprintf(buff + index, buff_len - index,
+ "%02X ", settings[i]);
+ }
+ pr_info("%s: Settings = %s\n", __func__, buff);
+ ret = fts_write(cmd1, 7);
+ if(ret >= OK)
+ ret = fts_write(cmd, 2 + size);
+ /* use write instead of writeFw because can be called while the
+ * interrupts are enabled */
+ if (ret < OK) {
+ pr_err("%s: write failed...ERROR %08X !\n", __func__, ret);
+ kfree(buff);
+ kfree(cmd);
+ return ret | ERROR_SET_FEATURE_FAIL;
+ }
+ pr_info("%s: Setting feature OK!\n", __func__);
+ kfree(cmd);
+ kfree(buff);
+ return OK;
+}
+/** @}*/
+
+/** @addtogroup sys_cmd
+ * @{
+ */
+/**
+ * Write a system command to the IC
+ * @param sys_cmd System Command to execute; possible values
+ * @link sys_opt System Command Option @endlink
+ * @param sett settings option for the selected system command
+ * (@link sys_special_opt Special Command Option @endlink, @link ito_opt
+ * ITO Test Option @endlink, @link load_opt Load Host Data Option @endlink)
+ * @param size in bytes of settings
+ * @return OK if success or an error code which specify the type of error
+ */
+int writeSysCmd(u8 sys_cmd, u8 *sett, int size)
+{
+ u8 *cmd;
+ int ret;
+ char *buff;
+ int buff_len = ((2 + 1) * size + 1) * sizeof(char);
+ int index = 0;
+ u8 cmd1[7] = {0xFA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ cmd = kzalloc((2 + size) * sizeof(u8), GFP_KERNEL);
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if ((buff == NULL) || (cmd == NULL)) {
+ kfree(buff);
+ kfree(cmd);
+ return ERROR_ALLOC;
+ }
+
+ cmd[0] = FTS_CMD_SYSTEM;
+ cmd[1] = sys_cmd;
+
+ for (ret = 0; ret < size; ret++) {
+ cmd[2 + ret] = sett[ret];
+ index += scnprintf(buff + index, buff_len - index,
+ "%02X ", sett[ret]);
+ }
+ pr_info("%s: Command = %02X %02X %s\n", __func__, cmd[0],
+ cmd[1], buff);
+ pr_info("%s: Writing Sys command...\n", __func__);
+ if (sys_cmd != SYS_CMD_LOAD_DATA) {
+ ret = fts_write(cmd1, 7);
+ if(ret >= OK)
+ ret = fts_writeFwCmd(cmd, 2 + size);
+ }
+ else {
+ if (size >= 1)
+ ret = requestSyncFrame(sett[0]);
+ else {
+ pr_err("%s: No setting argument! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ kfree(cmd);
+ kfree(buff);
+ return ERROR_OP_NOT_ALLOW;
+ }
+ }
+ if (ret < OK)
+ pr_err("%s: ERROR %08X\n", __func__, ret);
+ else
+ pr_info("%s: FINISHED!\n", __func__);
+
+ kfree(cmd);
+ kfree(buff);
+ return ret;
+}
+/** @}*/
+
+/** @addtogroup system_info
+ * @{
+ */
+/**
+ * Initialize the System Info Struct with default values according to the error
+ * found during the reading
+ * @param i2cError 1 if there was an I2C error while reading the System Info
+ * data from memory, other value if another error occurred
+ * @return OK if success or an error code which specify the type of error
+ */
+int defaultSysInfo(int i2cError)
+{
+ int i;
+
+ pr_info("Setting default System Info...\n");
+
+ if (i2cError == 1) {
+ systemInfo.u16_fwVer = 0xFFFF;
+ systemInfo.u16_cfgProjectId = 0xFFFF;
+ for (i = 0; i < RELEASE_INFO_SIZE; i++)
+ systemInfo.u8_releaseInfo[i] = 0xFF;
+ systemInfo.u16_cxVer = 0xFFFF;
+ } else {
+ systemInfo.u16_fwVer = 0x0000;
+ systemInfo.u16_cfgProjectId = 0x0000;
+ for (i = 0; i < RELEASE_INFO_SIZE; i++)
+ systemInfo.u8_releaseInfo[i] = 0x00;
+ systemInfo.u16_cxVer = 0x0000;
+ }
+
+ systemInfo.u8_scrRxLen = 0;
+ systemInfo.u8_scrTxLen = 0;
+
+ pr_info("default System Info DONE!\n");
+ return OK;
+}
+
+/**
+ * Read the System Info data from memory. System Info is loaded automatically
+ * after every system reset.
+ * @param request if 1, will be asked to the FW to reload the data, otherwise
+ * attempt to read it directly from memory
+ * @return OK if success or an error code which specify the type of error
+ */
+int readSysInfo(int request)
+{
+ int ret, i, index = 0;
+ u8 sett = LOAD_SYS_INFO;
+ u8 data[SYS_INFO_SIZE] = { 0 };
+ char temp[256] = { 0 };
+
+ if (request == 1) {
+ pr_info("%s: Requesting System Info...\n", __func__);
+
+ ret = writeSysCmd(SYS_CMD_LOAD_DATA, &sett, 1);
+ if (ret < OK) {
+ pr_err("%s: error while writing the sys cmd ERROR %08X\n",
+ __func__, ret);
+ goto FAIL;
+ }
+ }
+
+ pr_info("%s: Reading System Info...\n", __func__);
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16,
+ ADDR_FRAMEBUFFER, data, SYS_INFO_SIZE,
+ DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: error while reading the system data ERROR %08X\n",
+ __func__, ret);
+ goto FAIL;
+ }
+
+ pr_info("%s: Parsing System Info...\n", __func__);
+
+ if (data[0] != HEADER_SIGNATURE) {
+ pr_err("%s: The Header Signature is wrong! sign: %02X != %02X ERROR %08X\n",
+ __func__, data[0], HEADER_SIGNATURE,
+ ERROR_WRONG_DATA_SIGN);
+ ret = ERROR_WRONG_DATA_SIGN;
+ goto FAIL;
+ }
+
+
+ if (data[1] != LOAD_SYS_INFO) {
+ pr_err("%s: The Data ID is wrong! ids: %02X != %02X ERROR %08X\n",
+ __func__, data[3], LOAD_SYS_INFO,
+ ERROR_DIFF_DATA_TYPE);
+ ret = ERROR_DIFF_DATA_TYPE;
+ goto FAIL;
+ }
+
+ index += 4;
+ u8ToU16(&data[index], &systemInfo.u16_apiVer_rev);
+ index += 2;
+ systemInfo.u8_apiVer_minor = data[index++];
+ systemInfo.u8_apiVer_major = data[index++];
+ u8ToU16(&data[index], &systemInfo.u16_chip0Ver);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_chip0Id);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_chip1Ver);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_chip1Id);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_fwVer);
+ index += 2;
+ pr_info("FW VER = %04X\n", systemInfo.u16_fwVer);
+ u8ToU16(&data[index], &systemInfo.u16_svnRev);
+ index += 2;
+ pr_info("SVN REV = %04X\n", systemInfo.u16_svnRev);
+ u8ToU16(&data[index], &systemInfo.u16_cfgVer);
+ index += 2;
+ pr_info("CONFIG VER = %04X\n", systemInfo.u16_cfgVer);
+ u8ToU16(&data[index], &systemInfo.u16_cfgProjectId);
+ index += 2;
+ pr_info("CONFIG PROJECT ID = %04X\n", systemInfo.u16_cfgProjectId);
+ u8ToU16(&data[index], &systemInfo.u16_cxVer);
+ index += 2;
+ pr_info("CX VER = %04X\n", systemInfo.u16_cxVer);
+ u8ToU16(&data[index], &systemInfo.u16_cxProjectId);
+ index += 2;
+ pr_info("CX PROJECT ID = %04X\n", systemInfo.u16_cxProjectId);
+ systemInfo.u8_cfgAfeVer = data[index++];
+ systemInfo.u8_cxAfeVer = data[index++];
+ systemInfo.u8_panelCfgAfeVer = data[index++];
+ pr_info("AFE VER: CFG = %02X - CX = %02X - PANEL = %02X\n",
+ systemInfo.u8_cfgAfeVer, systemInfo.u8_cxAfeVer,
+ systemInfo.u8_panelCfgAfeVer);
+ systemInfo.u8_protocol = data[index++];
+ pr_info("Protocol = %02X\n", systemInfo.u8_protocol);
+ /* index+= 1; */
+ /* skip reserved area */
+
+ /* pr_err("Die Info = "); */
+ for (i = 0; i < DIE_INFO_SIZE; i++)
+ systemInfo.u8_dieInfo[i] = data[index++];
+
+ /* pr_err("\n"); */
+ pr_info("%s\n",
+ printHex("Die Info = ",
+ systemInfo.u8_dieInfo,
+ DIE_INFO_SIZE, temp, sizeof(temp)));
+ memset(temp, 0, 256);
+
+
+ /* pr_err("Release Info = "); */
+ for (i = 0; i < RELEASE_INFO_SIZE; i++)
+ systemInfo.u8_releaseInfo[i] = data[index++];
+
+ /* pr_err("\n"); */
+
+ pr_info("%s\n",
+ printHex("Release Info = ",
+ systemInfo.u8_releaseInfo,
+ RELEASE_INFO_SIZE,
+ temp,
+ sizeof(temp)));
+ memset(temp, 0, 256);
+
+ u8ToU32(&data[index], &systemInfo.u32_fwCrc);
+ index += 4;
+ u8ToU32(&data[index], &systemInfo.u32_cfgCrc);
+ index += 4;
+
+ index += 4; /* skip reserved area */
+
+ systemInfo.u8_mpFlag = data[index++];
+ pr_info("MP FLAG = %02X\n", systemInfo.u8_mpFlag);
+
+ index += 3 + 4; /* +3 remaining from mp flag address */
+
+ systemInfo.u8_ssDetScanSet = data[index];
+ pr_info("SS Detect Scan Select = %d\n",
+ systemInfo.u8_ssDetScanSet);
+ index += 4;
+
+ u8ToU16(&data[index], &systemInfo.u16_scrResX);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_scrResY);
+ index += 2;
+ pr_info("Screen Resolution = %d x %d\n",
+ systemInfo.u16_scrResX, systemInfo.u16_scrResY);
+ systemInfo.u8_scrTxLen = data[index++];
+ pr_info("TX Len = %d\n", systemInfo.u8_scrTxLen);
+ systemInfo.u8_scrRxLen = data[index++];
+ pr_info("RX Len = %d\n", systemInfo.u8_scrRxLen);
+ systemInfo.u8_keyLen = data[index++];
+ pr_info("Key Len = %d\n", systemInfo.u8_keyLen);
+ systemInfo.u8_forceLen = data[index++];
+ pr_info("Force Len = %d\n", systemInfo.u8_forceLen);
+ index += 8;
+
+ u8ToU32(&data[index], &systemInfo.u32_productionTimestamp);
+ pr_info("Production Timestamp = %08X\n",
+ systemInfo.u32_productionTimestamp);
+
+ index += 32; /* skip reserved area */
+
+ u8ToU16(&data[index], &systemInfo.u16_dbgInfoAddr);
+ index += 2;
+
+ index += 6; /* skip reserved area */
+
+ u8ToU16(&data[index], &systemInfo.u16_msTchRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_msTchFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_msTchStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_msTchBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssTchTxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchTxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchTxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchTxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssTchRxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchRxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchRxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssTchRxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_keyRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_keyFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_keyStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_keyBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_frcRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_frcFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_frcStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_frcBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrTxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrTxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrTxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrTxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrRxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrRxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrRxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssHvrRxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxTxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxTxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxTxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxTxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxRxRawAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxRxFilterAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxRxStrenAddr);
+ index += 2;
+ u8ToU16(&data[index], &systemInfo.u16_ssPrxRxBaselineAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssDetRawAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssDetFilterAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssDetStrenAddr);
+ index += 2;
+
+ u8ToU16(&data[index], &systemInfo.u16_ssDetBaselineAddr);
+ index += 2;
+
+ pr_info("Parsed %d bytes!\n", index);
+
+
+ if (index != SYS_INFO_SIZE) {
+ pr_err("%s: index = %d different from %d ERROR %08X\n",
+ __func__, index, SYS_INFO_SIZE,
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ pr_info("System Info Read DONE!\n");
+ return OK;
+
+FAIL:
+ defaultSysInfo(isI2cError(ret));
+ return ret;
+}
+/** @}*/
+
+
+/**
+ * Read data from the Config Memory
+ * @param offset Starting address in the Config Memory of data to read
+ * @param outBuf pointer of a byte array which contain the bytes to read
+ * @param len number of bytes to read
+ * @return OK if success or an error code which specify the type of error
+ */
+int readConfig(u16 offset, u8 *outBuf, int len)
+{
+ int ret;
+ u64 final_address = offset + ADDR_CONFIG_OFFSET;
+
+ pr_info("%s: Starting to read config memory at %llx ...\n",
+ __func__, final_address);
+ ret = fts_writeReadU8UX(FTS_CMD_CONFIG_R, BITS_16, final_address,
+ outBuf, len, DUMMY_CONFIG);
+ if (ret < OK) {
+ pr_err("%s: Impossible to read Config Memory... ERROR %08X!\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("%s: Read config memory FINISHED!\n", __func__);
+ return OK;
+}
+
+/**
+ * Write data into the Config Memory
+ * @param offset Starting address in the Config Memory where write the data
+ * @param data pointer of a byte array which contain the data to write
+ * @param len number of bytes to write
+ * @return OK if success or an error code which specify the type of error
+ */
+int writeConfig(u16 offset, u8 *data, int len)
+{
+ int ret;
+ u64 final_address = offset + ADDR_CONFIG_OFFSET;
+
+ pr_info("%s: Starting to write config memory at %llx ...\n",
+ __func__, final_address);
+ ret = fts_writeU8UX(FTS_CMD_CONFIG_W, BITS_16, final_address, data,
+ len);
+ if (ret < OK) {
+ pr_err("%s: Impossible to write Config Memory... ERROR %08X!\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("%s: Write config memory FINISHED!\n", __func__);
+ return OK;
+}
+
+/* Set the interrupt state
+ * @param enable Indicates whether interrupts should enabled.
+ * @return OK if success
+ */
+int fts_enableInterrupt(bool enable)
+{
+ struct fts_ts_info *info = NULL;
+ unsigned long flag;
+
+ if (getClient() == NULL) {
+ pr_err("Cannot get client irq. Error = %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+ info = dev_get_drvdata(&getClient()->dev);
+
+ spin_lock_irqsave(&info->fts_int, flag);
+
+ if (enable == info->irq_enabled)
+ pr_debug("Interrupt is already set (enable = %d).\n", enable);
+ else {
+ info->irq_enabled = enable;
+ if (enable) {
+ enable_irq(getClient()->irq);
+ pr_debug("Interrupt enabled.\n");
+ } else {
+ disable_irq_nosync(getClient()->irq);
+ pr_debug("Interrupt disabled.\n");
+ }
+ }
+
+ spin_unlock_irqrestore(&info->fts_int, flag);
+ return OK;
+}
+
+/**
+ * Check if there is a crc error in the IC which prevent the fw to run.
+ * @return OK if no CRC error, or a number >OK according the CRC error found
+ */
+int fts_crc_check(void)
+{
+ u8 val;
+ u8 crc_status;
+ int res;
+ u8 error_to_search[6] = { EVT_TYPE_ERROR_CRC_CFG_HEAD,
+ EVT_TYPE_ERROR_CRC_CFG,
+ EVT_TYPE_ERROR_CRC_CX,
+ EVT_TYPE_ERROR_CRC_CX_HEAD,
+ EVT_TYPE_ERROR_CRC_CX_SUB,
+ EVT_TYPE_ERROR_CRC_CX_SUB_HEAD };
+
+
+ res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, ADDR_CRC,
+ &val, 1, DUMMY_HW_REG);
+ /* read 2 bytes because the first one is a dummy byte! */
+ if (res < OK) {
+ pr_err("%s Cannot read crc status ERROR %08X\n", __func__, res);
+ return res;
+ }
+
+ crc_status = val & CRC_MASK;
+ if (crc_status != OK) { /* CRC error if crc_status!=0 */
+ pr_err("%s CRC ERROR = %02X\n", __func__, crc_status);
+ return CRC_CODE;
+ }
+
+ pr_info("%s: Verifying if Config CRC Error...\n", __func__);
+ res = fts_system_reset();
+ if (res >= OK) {
+ res = pollForErrorType(error_to_search, 2);
+ if (res < OK) {
+ pr_info("%s: No Config CRC Error Found!\n", __func__);
+ pr_info("%s: Verifying if Cx CRC Error...\n", __func__);
+ res = pollForErrorType(&error_to_search[2], 4);
+ if (res < OK) {
+ pr_info("%s: No Cx CRC Error Found!\n",
+ __func__);
+ return OK;
+ } else {
+ pr_err("%s: Cx CRC Error found! CRC ERROR = %02X\n",
+ __func__, res);
+ return CRC_CX;
+ }
+ } else {
+ pr_err("%s: Config CRC Error found! CRC ERROR = %02X\n",
+ __func__, res);
+ return CRC_CONFIG;
+ }
+ } else {
+ pr_err("%s: Error while executing system reset! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ return OK;
+}
+
+/**
+ * Request a host data and use the sync method to understand when the FW load
+ * it
+ * @param type the type ID of host data to load (@link load_opt Load Host Data
+ * Option @endlink)
+ * @return OK if success or an error code which specify the type of error
+ */
+int requestSyncFrame(u8 type)
+{
+ u8 request[3] = { FTS_CMD_SYSTEM, SYS_CMD_LOAD_DATA, type };
+ u8 readData[DATA_HEADER] = { 0 };
+ int ret, retry = 0, retry2 = 0, time_to_count;
+ int count, new_count;
+ u8 cmd[7] = {0xFA, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ pr_info("%s: Starting to get a sync frame...\n", __func__);
+
+ while (retry2 < RETRY_MAX_REQU_DATA) {
+ pr_info("%s: Reading count...\n", __func__);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16,
+ ADDR_FRAMEBUFFER, readData, DATA_HEADER,
+ DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: Error while reading count! ERROR %08X\n",
+ __func__, ret | ERROR_REQU_DATA);
+ ret |= ERROR_REQU_DATA;
+ retry2++;
+ continue;
+ }
+
+ if (readData[0] != HEADER_SIGNATURE)
+ pr_err("%s: Invalid Signature while reading count! ERROR %08X\n",
+ __func__, ret | ERROR_REQU_DATA);
+
+ count = (readData[3] << 8) | readData[2];
+ new_count = count;
+ pr_info("%s: Base count = %d\n", __func__, count);
+
+ pr_info("%s: Requesting frame %02X attempt = %d\n",
+ __func__, type, retry2 + 1);
+ ret = fts_write(cmd, 7);
+ if(ret >= OK)
+ ret = fts_write(request, ARRAY_SIZE(request));
+ if (ret >= OK) {
+ pr_info("%s: Polling for new count...\n", __func__);
+ time_to_count = TIMEOUT_REQU_DATA / TIMEOUT_RESOLUTION;
+ while (count == new_count && retry < time_to_count) {
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R,
+ BITS_16,
+ ADDR_FRAMEBUFFER,
+ readData,
+ DATA_HEADER,
+ DUMMY_FRAMEBUFFER);
+ if ((ret >= OK) && (readData[0] ==
+ HEADER_SIGNATURE) &&
+ (readData[1] == type))
+ new_count = ((readData[3] << 8) |
+ readData[2]);
+ else
+ pr_err("%s: invalid Signature or can not read count... ERROR %08X\n",
+ __func__, ret);
+ retry++;
+ mdelay(TIMEOUT_RESOLUTION);
+ }
+
+ if (count == new_count) {
+ pr_err("%s: New count not received! ERROR %08X\n",
+ __func__,
+ ERROR_TIMEOUT | ERROR_REQU_DATA);
+ ret = ERROR_TIMEOUT | ERROR_REQU_DATA;
+ } else {
+ pr_info("%s: New count found! count = %d! Frame ready!\n",
+ __func__, new_count);
+ return OK;
+ }
+ }
+ retry2++;
+ }
+ pr_err("%s: Request Data failed! ERROR %08X\n", __func__, ret);
+ return ret;
+}
+
+/**
+ * Set the Active Scanning Frequency to a defined value
+ * @param freq scanning frequency to set in Hz
+ * @return OK if success or an error code which specify the type of error
+ * @warning The scan frequency can be set only for the MS scan!
+ */
+int setActiveScanFrequency(u32 freq)
+{
+ int res;
+ u8 temp[2] = { 0 };
+ u16 t_cycle;
+
+ pr_info("%s: Setting the scanning frequency to %uHz...\n",
+ __func__, freq);
+
+ /* read MRN register */
+ res = readConfig(ADDR_CONFIG_MRN, temp, 1);
+ if (res < OK) {
+ pr_err("%s: error while reading mrn count! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ /* setting r count to 0 (= 1 R cycle used) and write it back */
+ temp[0] &= (~0x03);
+ res = writeConfig(ADDR_CONFIG_MRN, temp, 1);
+ if (res < OK) {
+ pr_err("%s: error while writing mrn count! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ /* set first R cycle slot according the specified frequency */
+ /* read T cycle */
+ res = readConfig(ADDR_CONFIG_T_CYCLE, temp, 2);
+ if (res < OK) {
+ pr_err("%s: error while reading T cycle! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+ t_cycle = ((u16)(temp[1] << 8)) | temp[0];
+
+
+ /* compute the value of R cycle according the formula
+ * scan_freq = 30Mhz/(2*(T_cycle+R_cycle)) */
+ temp[0] = (30000000) / (freq * 2) - t_cycle;
+ /* write R cycle in Config Area */
+ pr_info("%s: T cycle = %d (0x%04X) => R0 cycle = %d (0x%02X)\n",
+ __func__, t_cycle, t_cycle, temp[0], temp[0]);
+ res = writeConfig(ADDR_CONFIG_R0_CYCLE, temp, 1);
+ if (res < OK) {
+ pr_err("%s: error while writing R0 cycle! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ pr_info("%s: Saving Config into the flash ...\n", __func__);
+ /* save config */
+ temp[0] = SAVE_FW_CONF;
+ res = writeSysCmd(SYS_CMD_SAVE_FLASH, temp, 1);
+ if (res < OK) {
+ pr_err("%s: error while saving config into the flash! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ /* system reset */
+ res = fts_system_reset();
+ if (res < OK) {
+ pr_err("%s: error at system reset! ERROR %08X\n",
+ __func__, res);
+ return res;
+ }
+
+ pr_info("%s: Setting the scanning frequency FINISHED!\n", __func__);
+ return OK;
+}
+
+/**
+ * Write Host Data Memory
+ * @param type type of data to write
+ * @param data pointer to the data which are written
+ * @param msForceLen number of force (Tx) channels used with Mutual
+ * @param msSenseLen number of sense (Rx) channels used with Mutual
+ * @param ssForceLen number of force (Tx) channel used with Self
+ * @param ssSenseLen number of sense (Rx) channel used with Self
+ * @param save if =1 will save the host data written into the flash
+ * @return OK if success or an error code which specify the type of error
+ */
+int writeHostDataMemory(u8 type, u8 *data, u8 msForceLen, u8 msSenseLen,
+ u8 ssForceLen, u8 ssSenseLen, int save)
+{
+ int res;
+ int size = (msForceLen * msSenseLen) + (ssForceLen + ssSenseLen);
+ u8 sett = SPECIAL_WRITE_HOST_MEM_TO_FLASH;
+ u8 *temp;
+
+ temp = kzalloc((size + SYNCFRAME_DATA_HEADER) * sizeof(u8), GFP_KERNEL);
+ if (temp == NULL)
+ return ERROR_ALLOC;
+
+ pr_info("%s: Starting to write Host Data Memory\n", __func__);
+
+ temp[0] = 0x5A;
+ temp[1] = type;
+ temp[5] = msForceLen;
+ temp[6] = msSenseLen;
+ temp[7] = ssForceLen;
+ temp[8] = ssSenseLen;
+
+ memcpy(&temp[16], data, size);
+
+ pr_info("%s: Write Host Data Memory in buffer...\n", __func__);
+ res = fts_writeU8UX(FTS_CMD_FRAMEBUFFER_W, BITS_16,
+ ADDR_FRAMEBUFFER, temp, size +
+ SYNCFRAME_DATA_HEADER);
+
+ if (res < OK) {
+ pr_err("%s: error while writing the buffer! ERROR %08X\n",
+ __func__, res);
+ kfree(temp);
+ return res;
+ }
+
+ /* save host data memory into the flash */
+ if (save == 1) {
+ pr_info("%s: Trigger writing into the flash...\n", __func__);
+ res = writeSysCmd(SYS_CMD_SPECIAL, &sett, 1);
+ if (res < OK) {
+ pr_err("%s: error while writing into the flash! ERROR %08X\n",
+ __func__, res);
+ kfree(temp);
+ return res;
+ }
+ }
+
+
+ pr_info("%s: write Host Data Memory FINISHED!\n", __func__);
+ kfree(temp);
+ return OK;
+}
+
+/*
+ * Save MP flag value into the flash
+ * @param mpflag Value to write in the MP Flag field
+ * @return OK if success or an error code which specify the type of error
+ */
+int saveMpFlag(u8 mpflag)
+{
+ int ret = OK;
+ u8 panelCfg = SAVE_PANEL_CONF;
+
+ pr_info("%s: Saving MP Flag = %02X\n", __func__, mpflag);
+ ret |= writeSysCmd(SYS_CMD_MP_FLAG, &mpflag, 1);
+ if (ret < OK)
+ pr_err("%s: Error while writing MP flag on ram... ERROR %08X\n",
+ __func__, ret);
+
+ ret |= writeSysCmd(SYS_CMD_SAVE_FLASH, &panelCfg, 1);
+ if (ret < OK)
+ pr_err("%s: Error while saving MP flag on flash... ERROR %08X\n",
+ __func__, ret);
+
+ ret |= readSysInfo(1);
+ if (ret < OK) {
+ pr_err("%s: Error while refreshing SysInfo... ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("%s: Saving MP Flag OK!\n", __func__);
+ return OK;
+}
diff --git a/fts_lib/ftsCore.h b/fts_lib/ftsCore.h
new file mode 100644
index 0000000..0a4c9ae
--- /dev/null
+++ b/fts_lib/ftsCore.h
@@ -0,0 +1,216 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Core definitions **
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsCore.h
+ * \brief Contains all the definitions and structs of Core functionalities
+ */
+
+#ifndef FTS_CORE_H
+#define FTS_CORE_H
+
+#include "ftsHardware.h"
+#include "ftsSoftware.h"
+#include "../fts.h"
+
+/* HW DATA */
+#define GPIO_NOT_DEFINED -1 /* /< value assumed by reset_gpio when
+ * the reset pin of the IC is not
+ * connected */
+
+
+#define ADDR_SIZE_HW_REG BITS_32 /* /< value of AddrSize for Hw register
+ * in FTI @see AddrSize */
+
+#define DATA_HEADER 4 /* /< size in byte of the header loaded
+ * with the data in the frambuffer */
+
+/**
+ * Type of CRC errors
+ */
+typedef enum {
+ CRC_CODE = 1, /* /< CRC in the code section */
+ CRC_CONFIG = 2, /* /< CRC in the config section */
+ CRC_CX = 3, /* /< CRC in the cx section */
+ CRC_PANEL = 4 /* /< CRC in the panel section */
+} CRC_Error;
+
+/* CHIP INFO */
+/** @defgroup system_info System Info
+ * System Info Data collect the most important information about hw and fw
+ * @{
+ */
+/* Size in bytes of System Info data */
+#define SYS_INFO_SIZE 216 /* Num bytes of die info */
+#define DIE_INFO_SIZE 16 /* Num bytes of external release
+ * in config */
+#define EXTERNAL_RELEASE_INFO_SIZE 8 /* Num bytes of release info in
+ * sys info
+ * (first bytes are external
+ * release) */
+#define RELEASE_INFO_SIZE (EXTERNAL_RELEASE_INFO_SIZE)
+/** @}*/
+
+/* RETRY MECHANISM */
+#define RETRY_MAX_REQU_DATA 2 /* /< Max number of attempts
+ * performed
+ * when requesting data */
+#define RETRY_SYSTEM_RESET 3 /* /< Max number of attempts
+ * performed
+ * to reset the IC */
+
+/** @addtogroup system_info
+ * @{
+ */
+
+/**
+ * Struct which contains fundamental information about the chip and its
+ * configuration
+ */
+typedef struct {
+ u16 u16_apiVer_rev; /* /< API revision version */
+ u8 u8_apiVer_minor; /* /< API minor version */
+ u8 u8_apiVer_major; /* /< API major version */
+ u16 u16_chip0Ver; /* /< Dev0 version */
+ u16 u16_chip0Id; /* /< Dev0 ID */
+ u16 u16_chip1Ver; /* /< Dev1 version */
+ u16 u16_chip1Id; /* /< Dev1 ID */
+ u16 u16_fwVer; /* /< Fw version */
+ u16 u16_svnRev; /* /< SVN Revision */
+ u16 u16_cfgVer; /* /< Config Version */
+ u16 u16_cfgProjectId; /* /< Config Project ID */
+ u16 u16_cxVer; /* /< Cx Version */
+ u16 u16_cxProjectId; /* /< Cx Project ID */
+ u8 u8_cfgAfeVer; /* /< AFE version in Config */
+ u8 u8_cxAfeVer; /* /< AFE version in CX */
+ u8 u8_panelCfgAfeVer; /* /< AFE version in PanelMem */
+ u8 u8_protocol; /* /< Touch Report Protocol */
+ u8 u8_dieInfo[DIE_INFO_SIZE]; /* /< Die information */
+ u8 u8_releaseInfo[RELEASE_INFO_SIZE]; /* /< Release information */
+ u32 u32_fwCrc; /* /< Crc of FW */
+ u32 u32_cfgCrc; /* /< Crc of config */
+ u8 u8_mpFlag; /* /< MP Flag */
+ u8 u8_ssDetScanSet; /* /< Type of Detect Scan Selected */
+
+ u16 u16_scrResX;/* /< X resolution on main screen */
+ u16 u16_scrResY;/* /< Y resolution on main screen */
+ u8 u8_scrTxLen; /* /< Tx length */
+ u8 u8_scrRxLen; /* /< Rx length */
+ u8 u8_keyLen; /* /< Key Len */
+ u8 u8_forceLen; /* /< Force Len */
+ u32 u32_productionTimestamp; /* /< Production Timestamp */
+
+ u16 u16_dbgInfoAddr; /* /< Offset of debug Info structure */
+
+ u16 u16_msTchRawAddr; /* /< Offset of MS touch raw frame */
+ u16 u16_msTchFilterAddr;/* /< Offset of MS touch filter frame */
+ u16 u16_msTchStrenAddr; /* /< Offset of MS touch strength frame */
+ u16 u16_msTchBaselineAddr; /* /< Offset of MS touch baseline frame
+ * */
+
+ u16 u16_ssTchTxRawAddr; /* /< Offset of SS touch force raw frame */
+ u16 u16_ssTchTxFilterAddr; /* /< Offset of SS touch force filter
+ * frame */
+ u16 u16_ssTchTxStrenAddr;/* /< Offset of SS touch force strength frame
+ * */
+ u16 u16_ssTchTxBaselineAddr; /* /< Offset of SS touch force baseline
+ * frame */
+
+ u16 u16_ssTchRxRawAddr; /* /< Offset of SS touch sense raw frame */
+ u16 u16_ssTchRxFilterAddr; /* /< Offset of SS touch sense filter
+ * frame */
+ u16 u16_ssTchRxStrenAddr;/* /< Offset of SS touch sense strength frame
+ * */
+ u16 u16_ssTchRxBaselineAddr; /* /< Offset of SS touch sense baseline
+ * frame */
+
+ u16 u16_keyRawAddr; /* /< Offset of key raw frame */
+ u16 u16_keyFilterAddr; /* /< Offset of key filter frame */
+ u16 u16_keyStrenAddr; /* /< Offset of key strength frame */
+ u16 u16_keyBaselineAddr; /* /< Offset of key baseline frame */
+
+ u16 u16_frcRawAddr; /* /< Offset of force touch raw frame */
+ u16 u16_frcFilterAddr; /* /< Offset of force touch filter frame */
+ u16 u16_frcStrenAddr; /* /< Offset of force touch strength frame */
+ u16 u16_frcBaselineAddr;/* /< Offset of force touch baseline frame */
+
+ u16 u16_ssHvrTxRawAddr; /* /< Offset of SS hover Force raw frame */
+ u16 u16_ssHvrTxFilterAddr; /* /< Offset of SS hover Force filter
+ * frame */
+ u16 u16_ssHvrTxStrenAddr;/* /< Offset of SS hover Force strength frame
+ * */
+ u16 u16_ssHvrTxBaselineAddr; /* /< Offset of SS hover Force baseline
+ * frame */
+
+ u16 u16_ssHvrRxRawAddr; /* /< Offset of SS hover Sense raw frame */
+ u16 u16_ssHvrRxFilterAddr; /* /< Offset of SS hover Sense filter
+ * frame */
+ u16 u16_ssHvrRxStrenAddr; /* /< Offset of SS hover Sense strength
+ * frame */
+ u16 u16_ssHvrRxBaselineAddr; /* /< Offset of SS hover Sense baseline
+ * frame */
+
+ u16 u16_ssPrxTxRawAddr; /* /< Offset of SS proximity force raw frame */
+ u16 u16_ssPrxTxFilterAddr; /* /< Offset of SS proximity force
+ * filter frame */
+ u16 u16_ssPrxTxStrenAddr;/* /< Offset of SS proximity force strength
+ * frame */
+ u16 u16_ssPrxTxBaselineAddr; /* /< Offset of SS proximity force
+ * baseline frame */
+
+ u16 u16_ssPrxRxRawAddr; /* /< Offset of SS proximity sense raw frame */
+ u16 u16_ssPrxRxFilterAddr; /* /< Offset of SS proximity sense
+ * filter frame */
+ u16 u16_ssPrxRxStrenAddr;/* /< Offset of SS proximity sense strength
+ * frame */
+ u16 u16_ssPrxRxBaselineAddr; /* /< Offset of SS proximity sense
+ * baseline frame */
+
+ u16 u16_ssDetRawAddr; /* /< Offset of SS detect raw frame */
+ u16 u16_ssDetFilterAddr; /* /< Offset of SS detect filter
+ * frame */
+ u16 u16_ssDetStrenAddr; /* /< Offset of SS detect strength
+ * frame */
+ u16 u16_ssDetBaselineAddr; /* /< Offset of SS detect baseline
+ * frame */
+} SysInfo;
+
+/** @}*/
+
+int initCore(struct fts_ts_info *info);
+void setResetGpio(int gpio);
+int fts_system_reset(void);
+int isSystemResettedUp(void);
+int isSystemResettedDown(void);
+void setSystemResetedUp(int val);
+void setSystemResetedDown(int val);
+int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int
+ time_to_wait);
+int checkEcho(u8 *cmd, int size);
+int setScanMode(u8 mode, u8 settings);
+int setFeatures(u8 feat, u8 *settings, int size);
+int defaultSysInfo(int i2cError);
+int writeSysCmd(u8 sys_cmd, u8 *sett, int size);
+int readSysInfo(int request);
+int readConfig(u16 offset, u8 *outBuf, int len);
+int writeConfig(u16 offset, u8 *data, int len);
+int fts_enableInterrupt(bool enable);
+int fts_crc_check(void);
+int requestSyncFrame(u8 type);
+int setActiveScanFrequency(u32 freq);
+int writeHostDataMemory(u8 type, u8 *data, u8 msForceLen, u8 msSenseLen,
+ u8 ssForceLen, u8 ssSenseLen, int save);
+int saveMpFlag(u8 mpflag);
+#endif /* FTS_CORE_H */
diff --git a/fts_lib/ftsError.c b/fts_lib/ftsError.c
new file mode 100644
index 0000000..2ae4337
--- /dev/null
+++ b/fts_lib/ftsError.c
@@ -0,0 +1,349 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS error/info kernel log reporting *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsError.c
+ * \brief Contains all the function which handle with Error conditions
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+
+#include "../fts.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsIO.h"
+#include "ftsTool.h"
+#include "ftsCompensation.h"
+
+
+static ErrorList errors;/* /< private variable which implement the Error List */
+
+/**
+ * Check if an error code is related to an I2C failure
+ * @param error error code to check
+ * @return 1 if the first level error code is I2C related otherwise 0
+ */
+int isI2cError(int error)
+{
+ if (((error & 0x000000FF) >= (ERROR_BUS_R & 0x000000FF)) &&
+ ((error & 0x000000FF) <= (ERROR_BUS_O & 0x000000FF)))
+ return 1;
+ else
+ return 0;
+}
+
+
+/**
+ * Dump in the kernel log some debug info in case of FW hang
+ * @param outBuf (optional)pointer to bytes array where to copy the debug info,
+ * if NULL the data will just printed on the kernel log
+ * @param size dimension in bytes of outBuf,
+ * if > ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE, only the first
+ * ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE bytes will be copied
+ * @return OK if success or an error code which specify the type of error
+ */
+int dumpErrorInfo(u8 *outBuf, int size)
+{
+ int ret, i;
+ u8 data[ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE] = { 0 };
+ u32 sign = 0;
+
+ pr_err("%s: Starting dump of error info...\n", __func__);
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, ADDR_ERROR_DUMP,
+ data, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE,
+ DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("%s: reading data ERROR %08X\n", __func__,
+ ret);
+ return ret;
+ } else {
+ int buff_len, index = 0;
+ char *buff;
+
+ buff_len = (2 + 1) * ERROR_DUMP_COL_SIZE + 1;
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (outBuf != NULL) {
+ sign = size > ERROR_DUMP_ROW_SIZE *
+ ERROR_DUMP_COL_SIZE ? ERROR_DUMP_ROW_SIZE *
+ ERROR_DUMP_COL_SIZE : size;
+ memcpy(outBuf, data, sign);
+ pr_err("%s: error info copied in the buffer!\n",
+ __func__);
+ }
+ pr_err("%s: Error Info =\n", __func__);
+ u8ToU32(data, &sign);
+ if (sign != ERROR_DUMP_SIGNATURE)
+ pr_err("%s: Wrong Error Signature! Data may be invalid!\n",
+ __func__);
+ else
+ pr_err("%s: Error Signature OK! Data are valid!\n",
+ __func__);
+
+ for (i = 0; i < ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE;
+ i++) {
+ index += scnprintf(buff + index, buff_len - index,
+ "%02X ", data[i]);
+ if (i % ERROR_DUMP_COL_SIZE ==
+ (ERROR_DUMP_COL_SIZE - 1)) {
+ pr_err("%s: %d) %s\n", __func__,
+ i / ERROR_DUMP_COL_SIZE,
+ buff);
+ index = 0;
+ }
+ }
+
+ kfree(buff);
+ pr_err("%s: dump of error info FINISHED!\n", __func__);
+ return OK;
+ }
+}
+
+
+/**
+ * Implement recovery strategies to be used when an error event is found
+ * while polling the FIFO
+ * @param event error event found during the polling
+ * @param size size of event
+ * @return OK if the error event doesn't require any action or the recovery
+ * strategy doesn't have any impact in the possible procedure that trigger the
+ * error,
+ * otherwise return an error code which specify the kind of error.
+ * If ERROR_HANDLER_STOP_PROC the calling function must stop!
+ */
+int errorHandler(u8 *event, int size)
+{
+ int res = OK;
+ struct fts_ts_info *info = NULL;
+
+ if (getDev() != NULL)
+ info = dev_get_drvdata(getDev());
+
+ if (info != NULL && event != NULL && size > 1 &&
+ event[0] == EVT_ID_ERROR) {
+ pr_debug("errorHandler: Starting handling...\n");
+ addErrorIntoList(event, size);
+ switch (event[1]) { /* TODO: write an error log for
+ * undefined command subtype 0xBA */
+ case EVT_TYPE_ERROR_ESD: /* esd */
+ res = fts_chip_powercycle(info);
+ if (res < OK)
+ pr_err("errorHandler: Error performing powercycle ERROR %08X\n",
+ res);
+
+ res = fts_system_reset();
+ if (res < OK)
+ pr_err("errorHandler: Cannot reset the device ERROR %08X\n",
+ res);
+ res = (ERROR_HANDLER_STOP_PROC | res);
+ break;
+
+ case EVT_TYPE_ERROR_WATCHDOG: /* watchdog */
+ dumpErrorInfo(NULL, 0);
+ res = fts_system_reset();
+ if (res < OK)
+ pr_err("errorHandler: Cannot reset the device ERROR %08X\n",
+ res);
+ res = (ERROR_HANDLER_STOP_PROC | res);
+ break;
+
+ case EVT_TYPE_ERROR_ITO_FORCETOGND:
+ pr_err("errorHandler: Force Short to GND!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_SENSETOGND:
+ pr_err("errorHandler: Sense short to GND!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_FORCETOVDD:
+ pr_err("errorHandler: Force short to VDD!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_SENSETOVDD:
+ pr_err("errorHandler: Sense short to VDD!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_FORCE_P2P:
+ pr_err("errorHandler: Force Pin to Pin Short!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_SENSE_P2P:
+ pr_err("errorHandler: Sense Pin to Pin Short!\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_FORCEOPEN:
+ pr_err("errorHandler: Force Open !\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_SENSEOPEN:
+ pr_err("errorHandler: Sense Open !\n");
+ break;
+ case EVT_TYPE_ERROR_ITO_KEYOPEN:
+ pr_err("errorHandler: Key Open !\n");
+ break;
+
+ case EVT_TYPE_ERROR_FLASH_FAILED:
+ pr_err("errorHandler: Previous flash failed!\n");
+ info->reflash_fw = 1;
+ break;
+
+ default:
+ pr_debug("errorHandler: No Action taken!\n");
+ break;
+ }
+ pr_debug("errorHandler: handling Finished! res = %08X\n",
+ res);
+ return res;
+ } else {
+ pr_err("errorHandler: event Null or not correct size! ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+
+/**
+ * Add an error event into the Error List
+ * @param event error event to add
+ * @param size size of event
+ * @return OK
+ */
+int addErrorIntoList(u8 *event, int size)
+{
+ int i = 0;
+
+ pr_debug("Adding error in to ErrorList...\n");
+
+ memcpy(&errors.list[errors.last_index * FIFO_EVENT_SIZE], event, size);
+ i = FIFO_EVENT_SIZE - size;
+ if (i > 0) {
+ pr_info("Filling last %d bytes of the event with zero...\n", i);
+ memset(&errors.list[errors.last_index * FIFO_EVENT_SIZE + size],
+ 0, i);
+ }
+ pr_debug("Adding error in to ErrorList... FINISHED!\n");
+
+ errors.count += 1;
+ if (errors.count > FIFO_DEPTH)
+ pr_err("ErrorList is going in overflow... the first %d event(s) were override!\n",
+ errors.count - FIFO_DEPTH);
+ errors.last_index = (errors.last_index + 1) % FIFO_DEPTH;
+
+ return OK;
+}
+
+/**
+ * Reset the Error List setting the count and last_index to 0.
+ * @return OK
+ */
+int resetErrorList(void)
+{
+ errors.count = 0;
+ errors.last_index = 0;
+ memset(errors.list, 0, FIFO_DEPTH * FIFO_EVENT_SIZE);
+ /* if count is not considered is better reset also the list in order to
+ * avoid to read data previously copied into the list */
+ return OK;
+}
+
+/**
+ * Get the number of error events copied into the Error List
+ * @return the number of error events into the Error List
+ */
+int getErrorListCount(void)
+{
+ if (errors.count > FIFO_DEPTH)
+ return FIFO_DEPTH;
+ else
+ return errors.count;
+}
+
+/* in case of success return the index of the event found */
+/**
+ * Scroll the Error List looking for the event specified
+ * @param event_to_search event_to_search pointer to an array of int where
+ * each element correspond to a byte of the event to find. If the element
+ * of the array has value -1, the byte of the event, in the same position
+ * of the element is ignored.
+ * @param event_bytes size of event_to_search
+ * @return a value >=0 if the event is found which represent the index of
+ * the Error List where the event is located otherwise an error code
+ */
+int pollErrorList(int *event_to_search, int event_bytes)
+{
+ int i = 0, j = 0, find = 0;
+ int count = getErrorListCount();
+
+ pr_debug("Starting to poll ErrorList...\n");
+ while (find != 1 && i < count) {
+ find = 1;
+ for (j = 0; j < event_bytes; j++) {
+ if ((event_to_search[i] != -1) &&
+ ((int)errors.list[i * FIFO_EVENT_SIZE + j] !=
+ event_to_search[i])) {
+ find = 0;
+ break;
+ }
+ }
+ i++;
+ }
+ if (find == 1) {
+ pr_debug("Error Found into ErrorList!\n");
+ return i - 1; /* there is i++ at the end of the while */
+ } else {
+ pr_err("Error Not Found into ErrorList! ERROR %08X\n",
+ ERROR_TIMEOUT);
+ return ERROR_TIMEOUT;
+ }
+}
+
+
+
+/**
+ * Poll the Error List looking for any error types passed in the arguments.
+ * Return at the first match!
+ * @param list pointer to a list of error types to look for
+ * @param size size of list
+ * @return error type found if success or ERROR_TIMEOUT
+ */
+int pollForErrorType(u8 *list, int size)
+{
+ int i = 0, j = 0, find = 0;
+ int count = getErrorListCount();
+
+ pr_info("%s: Starting to poll ErrorList... count = %d\n",
+ __func__, count);
+ while (find != 1 && i < count) {
+ for (j = 0; j < size; j++) {
+ if (list[j] == errors.list[i * FIFO_EVENT_SIZE + 1]) {
+ find = 1;
+ break;
+ }
+ }
+ i++;
+ }
+ if (find == 1) {
+ pr_info("%s: Error Type %02X into ErrorList!\n",
+ __func__, list[j]);
+ return list[j];
+ } else {
+ pr_err("%s: Error Type Not Found into ErrorList! ERROR %08X\n",
+ __func__, ERROR_TIMEOUT);
+ return ERROR_TIMEOUT;
+ }
+}
diff --git a/fts_lib/ftsError.h b/fts_lib/ftsError.h
new file mode 100644
index 0000000..d3a539f
--- /dev/null
+++ b/fts_lib/ftsError.h
@@ -0,0 +1,236 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS error/info kernel log reporting *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsError.h
+ * \brief Contains all the definitions and structs which refer to Error
+ * conditions
+ */
+
+#ifndef FTS_ERROR_H
+#define FTS_ERROR_H
+
+#include "ftsHardware.h"
+#include "ftsSoftware.h"
+
+
+/** @defgroup error_codes Error Codes
+ * Error codes that can be reported by the driver functions.
+ * An error code is made up by 4 bytes, each byte indicate a logic error
+ * level.\n
+ * From the LSB to the MSB, the logic level increase going from a low level
+ * error (I2C,TIMEOUT) to an high level error (flashing procedure fail,
+ * production test fail etc)
+ * @{
+ */
+
+/* FIRST LEVEL ERROR CODE */
+/** @defgroup first_level First Level Error Code
+ * @ingroup error_codes
+ * Errors related to low level operation which are not under control of driver,
+ * such as: communication protocol (I2C/SPI), timeout, file operations ...
+ * @{
+ */
+#define OK (0x00000000) /* /< No ERROR */
+#define ERROR_ALLOC (0x80000001) /* /< allocation of memory
+ * failed */
+#define ERROR_BUS_R (0x80000002) /* /< i2c/spi read failed */
+#define ERROR_BUS_W (0x80000003) /* /< i2c/spi write failed */
+#define ERROR_BUS_WR (0x80000004) /* /< i2c/spi write/read
+ * failed */
+#define ERROR_BUS_O (0x80000005) /* /< error during
+ * opening an i2c device */
+#define ERROR_OP_NOT_ALLOW (0x80000006) /* /< operation not allowed */
+#define ERROR_TIMEOUT (0x80000007) /* /< timeout expired!
+ * exceed the max number
+ * of retries or the max
+ * waiting time */
+#define ERROR_FILE_NOT_FOUND (0x80000008) /* /< the file that i
+ * want to open is not found */
+#define ERROR_FILE_PARSE (0x80000009) /* /< error during parsing
+ * the file */
+#define ERROR_FILE_READ (0x8000000A) /* /< error during
+ * reading the file */
+#define ERROR_LABEL_NOT_FOUND (0x8000000B) /* /< label not found */
+#define ERROR_FW_NO_UPDATE (0x8000000C) /* /< fw in the chip
+ * newer than the one in
+ * the memmh */
+#define ERROR_FLASH_UNKNOWN (0x8000000D) /* /< flash status busy
+ * or unknown */
+/** @}*/
+
+/* SECOND LEVEL ERROR CODE */
+/** @defgroup second_level Second Level Error Code
+ * @ingroup error_codes
+ * Errors related to simple logic operations in the IC which require one
+ * command or which are part of a more complex procedure
+ * @{
+ */
+#define ERROR_DISABLE_INTER (0x80000200) /* /< unable to
+ * disable the
+ * interrupt */
+#define ERROR_ENABLE_INTER (0x80000300) /* /< unable to activate
+ * the interrupt */
+#define ERROR_READ_CONFIG (0x80000400) /* /< failed to read
+ * config memory */
+#define ERROR_GET_OFFSET (0x80000500) /* /< unable to
+ * read an offset from
+ * memory */
+#define ERROR_GET_FRAME_DATA (0x80000600) /* /< unable to
+ * retrieve the data of
+ * a required frame */
+#define ERROR_DIFF_DATA_TYPE (0x80000700) /* /< FW answers
+ * with an event that
+ * has a different
+ * address respect the
+ * request done */
+#define ERROR_WRONG_DATA_SIGN (0x80000800) /* /< the signature of
+ * the host data is not
+ * HEADER_SIGNATURE */
+#define ERROR_SET_SCAN_MODE_FAIL (0x80000900) /* /< setting
+ * the scanning mode
+ * failed
+ * (sense on/off
+ * etc...) */
+#define ERROR_SET_FEATURE_FAIL (0x80000A00) /* /< setting a specific
+ * feature failed */
+#define ERROR_SYSTEM_RESET_FAIL (0x80000B00) /* /< the command
+ * SYSTEM RESET
+ * failed */
+#define ERROR_FLASH_NOT_READY (0x80000C00) /* /< flash status not
+ * ready within a
+ * timeout */
+#define ERROR_FW_VER_READ (0x80000D00) /* /< unable to retrieve
+ * fw_vers or the
+ * config_id */
+#define ERROR_GESTURE_ENABLE_FAIL (0x80000E00) /* /< unable to
+ * enable/disable
+ * the gesture */
+#define ERROR_GESTURE_START_ADD (0x80000F00) /* /< unable to start to
+ * add custom gesture */
+#define ERROR_GESTURE_FINISH_ADD (0x80001000) /* /< unable to finish
+ * to add custom gesture
+ */
+#define ERROR_GESTURE_DATA_ADD (0x80001100) /* /< unable to add
+ * custom gesture data
+ * */
+#define ERROR_GESTURE_REMOVE (0x80001200) /* /< unable to remove
+ * custom gesture data
+ * */
+#define ERROR_FEATURE_ENABLE_DISABLE (0x80001300) /* /< unable to
+ * enable/disable a
+ * feature mode in
+ * the IC */
+#define ERROR_NOISE_PARAMETERS (0x80001400) /* /< unable to set/read
+ * noise parameter in
+ * the IC */
+#define ERROR_CH_LEN (0x80001500) /* /< unable to retrieve
+ * the force and/or
+ * sense length */
+#define ERROR_INFO_BLOCK (0x80001600) /* /< info
+ * block
+ * corruption */
+/** @}*/
+
+/* THIRD LEVEL ERROR CODE */
+/** @defgroup third_level Third Level Error Code
+ * @ingroup error_codes
+ * Errors related to logic operations in the IC which require more
+ * commands/steps or which are part of a more complex procedure
+ * @{
+ */
+#define ERROR_REQU_HDM_DOWNLOAD (0x80010000) /* /< HDM download
+ * request failed */
+#define ERROR_REQU_DATA (0x80020000) /* /< data request
+ * failed */
+#define ERROR_HDM_DATA_HEADER (0x80030000) /* /< unable to retrieve
+ * the HDM data
+ * header */
+#define ERROR_COMP_DATA_GLOBAL (0x80040000) /* /< unable to retrieve
+ * the global
+ * compensation data */
+#define ERROR_COMP_DATA_NODE (0x80050000) /* /< unable to retrieve
+ * the compensation data
+ * for each node */
+#define ERROR_TEST_CHECK_FAIL (0x80060000) /* /< check of
+ * production limits or
+ * of fw answers failed */
+#define ERROR_MEMH_READ (0x80070000) /* /< memh reading
+ * failed */
+#define ERROR_FLASH_BURN_FAILED (0x80080000) /* /< flash burn failed */
+#define ERROR_MS_TUNING (0x80090000) /* /< ms tuning failed */
+#define ERROR_SS_TUNING (0x800A0000) /* /< ss tuning failed */
+#define ERROR_LP_TIMER_TUNING (0x800B0000) /* /< lp timer
+ * calibration failed */
+#define ERROR_SAVE_CX_TUNING (0x800C0000) /* /< save cx data to
+ * flash failed */
+#define ERROR_HANDLER_STOP_PROC (0x800D0000) /* /< stop the poll of the FIFO
+ * if particular errors are
+ * found */
+#define ERROR_CHECK_ECHO_FAIL (0x800E0000) /* /< unable to retrieve
+ * echo event */
+#define ERROR_GET_FRAME (0x800F0000) /* /< unable to get frame */
+#define ERROR_GOLDEN_MUTUAL_DATA (0x80100000) /* /< unable to read Golden
+ * Mutual Data */
+/** @}*/
+
+/* FOURTH LEVEL ERROR CODE */
+/** @defgroup fourth_level Fourth Level Error Code
+ * @ingroup error_codes
+ * Errors related to the highest logic operations in the IC which have an
+ * important impact on the driver flow or which require several commands and
+ * steps to be executed
+ * @{
+ */
+#define ERROR_PROD_TEST_DATA (0x81000000) /* /< production data
+ * test failed */
+#define ERROR_FLASH_PROCEDURE (0x82000000) /* /< fw update
+ * procedure failed */
+#define ERROR_PROD_TEST_ITO (0x83000000) /* /< production
+ * ito test failed */
+#define ERROR_PROD_TEST_INITIALIZATION (0x84000000) /* /< production
+ * initialization
+ * test failed */
+#define ERROR_GET_INIT_STATUS (0x85000000) /* /< mismatch of the MS or
+ * SS tuning_version */
+/** @}*/
+
+/** @}*/ /* end of error_commands section */
+
+/**
+ * Struct which store an ordered list of the errors events encountered during
+ *the polling of a FIFO.
+ * The max number of error events that can be stored is equal to FIFO_DEPTH
+ */
+typedef struct {
+ u8 list[FIFO_DEPTH * FIFO_EVENT_SIZE]; /* /< byte array which contains
+ * the series of error events
+ * encountered from the last
+ * reset of the list. */
+ int count; /* /< number of error events stored in the list */
+ int last_index; /* /< index of the list where will be stored the next
+ * error event. Subtract -1 to have the index of the
+ * last error event! */
+} ErrorList;
+
+int isI2cError(int error);
+int dumpErrorInfo(u8 *outBuf, int size);
+int errorHandler(u8 *event, int size);
+int addErrorIntoList(u8 *event, int size);
+int getErrorListCount(void);
+int resetErrorList(void);
+int pollErrorList(int *event_to_search, int event_bytes);
+int pollForErrorType(u8 *list, int size);
+#endif
diff --git a/fts_lib/ftsFlash.c b/fts_lib/ftsFlash.c
new file mode 100644
index 0000000..f57de07
--- /dev/null
+++ b/fts_lib/ftsFlash.c
@@ -0,0 +1,1123 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS API for Flashing the IC *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+
+/*!
+ * \file ftsFlash.c
+ * \brief Contains all the functions to handle the FW update process
+ */
+
+#include "ftsCore.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 */
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/serio.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+
+
+#ifdef FW_H_FILE
+#include "../fts_fw.h"
+#endif
+
+
+extern SysInfo systemInfo; /* /< forward declaration of the global variable
+ * of containing System Info Data */
+
+
+/**
+ * Retrieve the actual FW data from the system (bin file or header file)
+ * @param pathToFile name of FW file to load or "NULL" if the FW data should be
+ * loaded by a .h file
+ * @param data pointer to the pointer which will contains the FW data
+ * @param size pointer to a variable which will contain the size of the loaded
+ * data
+ * @return OK if success or an error code which specify the type of error
+ */
+int getFWdata(const char *pathToFile, u8 **data, int *size)
+{
+ const struct firmware *fw = NULL;
+ struct device *dev = getDev();
+ struct fts_ts_info *info = NULL;
+ int res, from = 0;
+ char *path = (char *)pathToFile;
+
+ if (dev != NULL)
+ info = dev_get_drvdata(dev);
+
+ pr_info("getFWdata starting ...\n");
+ if (strncmp(pathToFile, "NULL", 4) == 0) {
+ from = 1;
+ if (info != NULL && info->board)
+ path = (char *)info->board->fw_name;
+ else
+ path = PATH_FILE_FW;
+ }
+ /* keep the switch case because if the argument passed is null but
+ * the option from .h is not set we still try to load from bin */
+ switch (from) {
+#ifdef FW_H_FILE
+ case 1:
+ pr_info("Read FW from .h file!\n");
+ *size = FW_SIZE_NAME;
+ *data = (u8 *)kmalloc((*size) * sizeof(u8), GFP_KERNEL);
+ if (*data == NULL) {
+ pr_err("getFWdata: Impossible to allocate memory! ERROR %08X\n",
+ ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+ memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size));
+
+ break;
+#endif
+ default:
+ pr_info("Read FW from BIN file %s !\n", path);
+
+ if (dev != NULL) {
+ res = request_firmware(&fw, path, dev);
+ if (res == 0) {
+ *size = fw->size;
+ *data = (u8 *)kmalloc((*size) * sizeof(u8),
+ GFP_KERNEL);
+ if (*data == NULL) {
+ pr_err("getFWdata: Impossible to allocate memory! ERROR %08X\n",
+ ERROR_ALLOC);
+ release_firmware(fw);
+ return ERROR_ALLOC;
+ }
+ memcpy(*data, (u8 *)fw->data, (*size));
+ release_firmware(fw);
+ } else {
+ pr_err("getFWdata: No File found! ERROR %08X\n",
+ ERROR_FILE_NOT_FOUND);
+ return ERROR_FILE_NOT_FOUND;
+ }
+ } else {
+ pr_err("getFWdata: No device found! ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+ }
+
+ pr_info("getFWdata Finished!\n");
+ return OK;
+}
+
+
+/**
+ * Perform all the steps to read the FW that should be burnt in the IC from
+ * the system and parse it in order to fill a Firmware struct with the relevant
+ * info
+ * @param path name of FW file to load or "NULL" if the FW data should be
+ * loaded by a .h file
+ * @param fw pointer to a Firmware variable which will contains the FW data and
+ * info
+ * @param keep_cx if 1, the CX area will be loaded otherwise will be skipped
+ * @return OK if success or an error code which specify the type of error
+ */
+int readFwFile(const char *path, Firmware *fw, int keep_cx)
+{
+ int res;
+ int orig_size;
+ u8 *orig_data = NULL;
+
+
+ res = getFWdata(path, &orig_data, &orig_size);
+ if (res < OK) {
+ pr_err("readFwFile: impossible retrieve FW... ERROR %08X\n",
+ ERROR_MEMH_READ);
+ return res | ERROR_MEMH_READ;
+ }
+ res = parseBinFile(orig_data, orig_size, fw, keep_cx);
+ if (res < OK) {
+ pr_err("readFwFile: impossible parse ERROR %08X\n",
+ ERROR_MEMH_READ);
+ return res | ERROR_MEMH_READ;
+ }
+
+ return OK;
+}
+
+/**
+ * Perform all the steps necessary to burn the FW into the IC
+ * @param path name of FW file to load or "NULL" if the FW data should be
+ * loaded by a .h file
+ * @param force if 1, the flashing procedure will be forced and executed
+ * regardless the additional info, otherwise the FW in the file will be burnt
+ * only if it is newer than the one running in the IC
+ * @param keep_cx if 1, the CX area will be loaded and burnt otherwise will be
+ * skipped and the area will be untouched
+ * @return OK if success or an error code which specify the type of error
+ */
+int flashProcedure(const char *path, int force, int keep_cx)
+{
+ Firmware fw;
+ int res;
+
+ fw.data = NULL;
+ pr_info("Reading Fw file...\n");
+ res = readFwFile(path, &fw, keep_cx);
+ if (res < OK) {
+ pr_err("flashProcedure: ERROR %08X\n",
+ (res | ERROR_FLASH_PROCEDURE));
+ kfree(fw.data);
+ return res | ERROR_FLASH_PROCEDURE;
+ }
+ pr_info("Fw file read COMPLETED!\n");
+
+ pr_info("Starting flashing procedure...\n");
+ res = flash_burn(fw, force, keep_cx);
+ if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) {
+ pr_err("flashProcedure: ERROR %08X\n",
+ ERROR_FLASH_PROCEDURE);
+ kfree(fw.data);
+ return res | ERROR_FLASH_PROCEDURE;
+ }
+ pr_info("flashing procedure Finished!\n");
+ kfree(fw.data);
+
+ return res;
+}
+
+/**
+ * Poll the Flash Status Registers after the execution of a command to check
+ * if the Flash becomes ready within a timeout
+ * @param type register to check according to the previous command sent
+ * @return OK if success or an error code which specify the type of error
+ */
+int wait_for_flash_ready(u8 type)
+{
+ u8 cmd[5] = { FTS_CMD_HW_REG_R, 0x20, 0x00, 0x00, type };
+
+ u8 readData[2] = { 0 };
+ int i, res = -1;
+
+ pr_info("Waiting for flash ready ...\n");
+ for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) {
+ res = fts_writeRead(cmd, ARRAY_SIZE(cmd), readData, 2);
+ if (res < OK)
+ pr_err("wait_for_flash_ready: ERROR %08X\n",
+ ERROR_BUS_W);
+ else {
+#ifdef I2C_INTERFACE /* in case of spi there is a dummy byte */
+ res = readData[0] & 0x80;
+#else
+ res = readData[1] & 0x80;
+#endif
+
+ pr_info("flash status = %d\n", res);
+ }
+ mdelay(FLASH_WAIT_BEFORE_RETRY);
+ }
+
+ if (i == FLASH_RETRY_COUNT && res != 0) {
+ pr_err("Wait for flash TIMEOUT! ERROR %08X\n",
+ ERROR_TIMEOUT);
+ return ERROR_TIMEOUT;
+ }
+
+ pr_info("Flash READY!\n");
+ return OK;
+}
+
+
+/**
+ * Put the M3 in hold
+ * @return OK if success or an error code which specify the type of error
+ */
+int hold_m3(void)
+{
+ int ret;
+ u8 cmd[1] = { 0x01 };
+
+ pr_info("Command m3 hold...\n");
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_SYSTEM_RESET, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: ERROR %08X\n", __func__, ret);
+ return ret;
+ }
+ pr_info("Hold M3 DONE!\n");
+
+#if !defined(I2C_INTERFACE)
+ if (getClient() &&
+ (getClient()->mode & SPI_3WIRE) == 0) {
+ /* configure manually SPI4 because when no fw is running the
+ * chip use SPI3 by default */
+ pr_info("Setting SPI4 mode...\n");
+ cmd[0] = 0x10;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_GPIO_DIRECTION, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set gpio dir ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ cmd[0] = 0x02;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_GPIO_PULLUP, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set gpio pull-up ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+#ifdef ALIX
+ cmd[0] = 0x70;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_GPIO_CONFIG_REG3, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set gpio config ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+#else
+ cmd[0] = 0x07;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_GPIO_CONFIG_REG2, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set gpio config ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+#endif
+
+ cmd[0] = 0x30;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_GPIO_CONFIG_REG0, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set gpio config ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ cmd[0] = SPI4_MASK;
+ ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG,
+ ADDR_ICR, cmd, 1);
+ if (ret < OK) {
+ pr_err("%s: can not set spi4 mode ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+ mdelay(1); /* wait for the GPIO to stabilize */
+ }
+#endif
+
+ return OK;
+}
+
+
+
+
+/**
+ * Parse the raw data read from a FW file in order to fill properly the fields
+ * of a Firmware variable
+ * @param fw_data raw FW data loaded from system
+ * @param fw_size size of fw_data
+ * @param fwData pointer to a Firmware variable which will contain the
+ * processed data
+ * @param keep_cx if 1, the CX area will be loaded and burnt otherwise will be
+ * skipped and the area will be untouched
+ * @return OK if success or an error code which specify the type of error
+ */
+int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx)
+{
+ int dimension, index = 0;
+ u32 temp;
+ int res, i;
+ char buff[(2 + 1) * EXTERNAL_RELEASE_INFO_SIZE + 1];
+ int buff_len = sizeof(buff);
+ int buff_index = 0;
+
+ /* the file should contain at least the header plus the content_crc */
+ if (fw_size < FW_HEADER_SIZE + FW_BYTES_ALIGN || fw_data == NULL) {
+ pr_err("parseBinFile: Read only %d instead of %d... ERROR %08X\n",
+ fw_size, FW_HEADER_SIZE + FW_BYTES_ALIGN,
+ ERROR_FILE_PARSE);
+ res = ERROR_FILE_PARSE;
+ goto END;
+ } else {
+ /* start parsing of bytes */
+ u8ToU32(&fw_data[index], &temp);
+ if (temp != FW_HEADER_SIGNATURE) {
+ pr_err("parseBinFile: Wrong Signature %08X ... ERROR %08X\n",
+ temp, ERROR_FILE_PARSE);
+ res = ERROR_FILE_PARSE;
+ goto END;
+ }
+ pr_info("parseBinFile: Fw Signature OK!\n");
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ if (temp != FW_FTB_VER) {
+ pr_err("parseBinFile: Wrong ftb_version %08X ... ERROR %08X\n",
+ temp, ERROR_FILE_PARSE);
+ res = ERROR_FILE_PARSE;
+ goto END;
+ }
+ pr_info("parseBinFile: ftb_version OK!\n");
+ index += FW_BYTES_ALIGN;
+ if (fw_data[index] != DCHIP_ID_0 || fw_data[index + 1] !=
+ DCHIP_ID_1) {
+ pr_err("parseBinFile: Wrong target %02X != %02X %02X != %02X ... ERROR %08X\n",
+ fw_data[index], DCHIP_ID_0,
+ fw_data[index + 1],
+ DCHIP_ID_1, ERROR_FILE_PARSE);
+ res = ERROR_FILE_PARSE;
+ goto END;
+ }
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ pr_info("parseBinFile: FILE SVN REV = %08X\n", temp);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ fwData->fw_ver = temp;
+ pr_info("parseBinFile: FILE Fw Version = %04X\n",
+ fwData->fw_ver);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ pr_info("parseBinFile: FILE Config Project ID = %08X\n", temp);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ fwData->config_ver = temp;
+ pr_info("parseBinFile: FILE Config Version = %08X\n",
+ fwData->config_ver);
+
+ index += FW_BYTES_ALIGN * 2; /* skip reserved data */
+
+ index += FW_BYTES_ALIGN;
+ for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) {
+ fwData->externalRelease[i] = fw_data[index++];
+ buff_index += scnprintf(buff + buff_index,
+ buff_len - buff_index,
+ "%02X ",
+ fwData->externalRelease[i]);
+ }
+ pr_info("parseBinFile: File External Release = %s\n", buff);
+
+ /* index+=FW_BYTES_ALIGN; */
+ u8ToU32(&fw_data[index], &temp);
+ fwData->sec0_size = temp;
+ pr_info("parseBinFile: sec0_size = %08X (%d bytes)\n",
+ fwData->sec0_size, fwData->sec0_size);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ fwData->sec1_size = temp;
+ pr_info("parseBinFile: sec1_size = %08X (%d bytes)\n",
+ fwData->sec1_size, fwData->sec1_size);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ fwData->sec2_size = temp;
+ pr_info("parseBinFile: sec2_size = %08X (%d bytes)\n",
+ fwData->sec2_size, fwData->sec2_size);
+
+ index += FW_BYTES_ALIGN;
+ u8ToU32(&fw_data[index], &temp);
+ fwData->sec3_size = temp;
+ pr_info("parseBinFile: sec3_size = %08X (%d bytes)\n",
+ fwData->sec3_size, fwData->sec3_size);
+
+ index += FW_BYTES_ALIGN;/* skip header crc */
+
+ /* if (!keep_cx) */
+ /* { */
+ dimension = fwData->sec0_size + fwData->sec1_size +
+ fwData->sec2_size + fwData->sec3_size;
+ temp = fw_size;
+ /*} else
+ * {
+ * 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) {
+ pr_err("parseBinFile: Read only %d instead of %d... ERROR %08X\n",
+ fw_size, dimension + FW_HEADER_SIZE +
+ FW_BYTES_ALIGN, ERROR_FILE_PARSE);
+ res = ERROR_FILE_PARSE;
+ goto END;
+ }
+
+ fwData->data = (u8 *)kmalloc(dimension * sizeof(u8),
+ GFP_KERNEL);
+ if (fwData->data == NULL) {
+ pr_err("parseBinFile: ERROR %08X\n", ERROR_ALLOC);
+ res = ERROR_ALLOC;
+ goto END;
+ }
+
+ index += FW_BYTES_ALIGN;
+ memcpy(fwData->data, &fw_data[index], dimension);
+ if (fwData->sec2_size != 0)
+ u8ToU16(&fwData->data[fwData->sec0_size +
+ fwData->sec1_size +
+ FW_CX_VERSION], &fwData->cx_ver);
+
+ else {
+ pr_err("parseBinFile: Initialize cx_ver to default value!\n");
+ fwData->cx_ver = systemInfo.u16_cxVer;
+ }
+
+ pr_info("parseBinFile: CX Version = %04X\n", fwData->cx_ver);
+
+ fwData->data_size = dimension;
+ index = FLASH_ORG_INFO_INDEX;
+ fwData->fw_code_size = fw_data[index++];
+ fwData->panel_config_size = fw_data[index++];
+ fwData->cx_area_size = fw_data[index++];
+ fwData->fw_config_size = fw_data[index];
+
+ pr_info("parseBinFile: Code Pages: %d panel area Pages: %d"
+ " cx area Pages: %d fw config Pages: %d !\n",
+ fwData->fw_code_size, fwData->panel_config_size,
+ fwData->cx_area_size, fwData->fw_config_size);
+
+ if ((fwData->fw_code_size == 0) ||
+ (fwData->panel_config_size == 0) ||
+ (fwData->cx_area_size == 0) ||
+ (fwData->fw_config_size == 0)) {
+ pr_info("parseBinFile: Using default flash Address\n");
+ fwData->code_start_addr = FLASH_ADDR_CODE;
+ fwData->cx_start_addr = FLASH_ADDR_CX;
+ fwData->config_start_addr = FLASH_ADDR_CONFIG;
+ } else {
+ fwData->code_start_addr = FLASH_ADDR_CODE;
+ fwData->cx_start_addr = (FLASH_ADDR_CODE +
+ (((fwData->fw_code_size +
+ fwData->panel_config_size) *
+ FLASH_PAGE_SIZE) / 4));
+ fwData->config_start_addr = (FLASH_ADDR_CODE +
+ (((fwData->fw_code_size +
+ fwData->panel_config_size +
+ fwData->cx_area_size) *
+ FLASH_PAGE_SIZE) / 4));
+ }
+
+ pr_info("parseBinFile: Code start addr: 0x%08X"
+ " cx start addr: 0x%08X"
+ " fw start addr: 0x%08X !\n",
+ fwData->code_start_addr, fwData->cx_start_addr,
+ fwData->config_start_addr);
+
+ pr_info("READ FW DONE %d bytes!\n", fwData->data_size);
+ res = OK;
+ goto END;
+ }
+
+END:
+ kfree(fw_data);
+ return res;
+}
+
+/*
+ * Enable UVLO and Auto Power Down Mode
+ * @return OK if success or an error code which specify the type of error
+ */
+int flash_enable_uvlo_autopowerdown(void)
+{
+ u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_UVLO_ENABLE_CODE0,
+ FLASH_UVLO_ENABLE_CODE1 };
+ u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_AUTOPOWERDOWN_ENABLE_CODE0,
+ FLASH_AUTOPOWERDOWN_ENABLE_CODE1 };
+
+ pr_info("Command enable uvlo ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+ if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ pr_info("Enable uvlo and flash auto power down DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Unlock the flash to be programmed
+ * @return OK if success or an error code which specify the type of error
+ */
+int flash_unlock(void)
+{
+ u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1 };
+
+ u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_UNLOCK_CODE2, FLASH_UNLOCK_CODE3 };
+
+ pr_info("Command unlock ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) {
+ pr_err("flash_unlock: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) {
+ pr_err("Command unlock: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ pr_info("Unlock flash DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Unlock the flash to be erased
+ * @return OK if success or an error code which specify the type of error
+ */
+int flash_erase_unlock(void)
+{
+ u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00,
+ 0x00,
+ FLASH_ERASE_UNLOCK_CODE0, FLASH_ERASE_UNLOCK_CODE1 };
+
+ pr_info("Try to erase unlock flash...\n");
+
+ pr_info("Command erase unlock ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < 0) {
+ pr_err("flash_erase_unlock: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ pr_info("Erase Unlock flash DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Erase the full flash
+ * @return OK if success or an error code which specify the type of error
+ */
+int flash_full_erase(void)
+{
+ int status;
+
+ u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_ERASE_CODE0 + 1, 0x00 };
+ u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_ERASE_CODE0, FLASH_ERASE_CODE1 };
+
+ if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) {
+ pr_err("flash_erase_page_by_page: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ pr_info("Command full erase sent ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) {
+ pr_err("flash_full_erase: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ status = wait_for_flash_ready(FLASH_ERASE_CODE0);
+
+ if (status != OK) {
+ pr_err("flash_full_erase: ERROR %08X\n", ERROR_FLASH_NOT_READY);
+ return status | ERROR_FLASH_NOT_READY;
+ /* Flash not ready within the chosen time, better exit! */
+ }
+
+ pr_info("Full Erase flash DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Erase the flash page by page, giving the possibility to skip the CX area and
+ * maintain therefore its value
+ * @param keep_cx if SKIP_PANEL_INIT the Panel Init pages will be skipped,
+ * if > SKIP_PANEL_CX_INIT Cx and Panel Init pages otherwise all the pages will
+ * be deleted
+ * @return OK if success or an error code which specify the type of error
+ */
+int flash_erase_page_by_page(ErasePage keep_cx, Firmware *fw)
+{
+ u8 status, i = 0;
+
+ u8 flash_cx_start_page = FLASH_CX_PAGE_START;
+ u8 flash_cx_end_page = FLASH_CX_PAGE_END;
+ u8 flash_panel_start_page = FLASH_PANEL_PAGE_START;
+ u8 flash_panel_end_page = FLASH_PANEL_PAGE_END;
+
+ u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00,
+ FLASH_ERASE_CODE0 + 1, 0x00 };
+ u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_ERASE_CODE0,
+ 0xA0 };
+ u8 cmd2[9] = { FTS_CMD_HW_REG_W, 0x20, 0x00,
+ 0x01, 0x28,
+ 0xFF,
+ 0xFF, 0xFF, 0xFF };
+ u8 mask[4] = { 0 };
+ char buff[(2 + 1) * 4 + 1];
+ int buff_len = sizeof(buff);
+ int index = 0;
+
+ if ((fw->fw_code_size == 0) ||
+ (fw->panel_config_size == 0) ||
+ (fw->cx_area_size == 0) || (fw->fw_config_size == 0)) {
+ pr_info(" using default page address!\n");
+ } else {
+ flash_panel_start_page = fw->fw_code_size;
+ if (fw->panel_config_size > 1)
+ flash_panel_end_page = flash_panel_start_page +
+ (fw->panel_config_size - 1);
+ else
+ flash_panel_end_page = flash_panel_start_page;
+
+ flash_cx_start_page = flash_panel_end_page + 1;
+ if (fw->cx_area_size > 1)
+ flash_cx_end_page = flash_cx_start_page +
+ (fw->cx_area_size - 1);
+ else
+ flash_cx_end_page = flash_cx_start_page;
+ }
+
+ pr_info(" CX Start page: %d CX end page: %d Panel Start Page: %d"
+ "Panel End page: %d!\n", flash_cx_start_page,
+ flash_cx_end_page, flash_panel_start_page,
+ flash_panel_end_page);
+
+ for (i = flash_cx_start_page; i <= flash_cx_end_page && keep_cx >=
+ SKIP_PANEL_CX_INIT; i++) {
+ pr_info("Skipping erase CX page %d!\n", i);
+ fromIDtoMask(i, mask, 4);
+ }
+
+
+ for (i = flash_panel_start_page; i <= flash_panel_end_page && keep_cx >=
+ SKIP_PANEL_INIT; i++) {
+ pr_info("Skipping erase Panel Init page %d!\n", i);
+ fromIDtoMask(i, mask, 4);
+ }
+
+ for (i = 0; i < 4; i++) {
+ cmd2[5 + i] = cmd2[5 + i] & (~mask[i]);
+ index += scnprintf(buff + index, buff_len - index,
+ "%02X ", cmd2[5 + i]);
+ }
+ pr_info("Setting the page mask = %s\n", buff);
+
+ pr_info("Writing page mask...\n");
+ if (fts_write(cmd2, ARRAY_SIZE(cmd2)) < OK) {
+ pr_err("flash_erase_page_by_page: Page mask ERROR %08X\n",
+ ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) {
+ pr_err("flash_erase_page_by_page: Disable info ERROR %08X\n",
+ ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ pr_info("Command erase pages sent ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) {
+ pr_err("flash_erase_page_by_page: Erase ERROR %08X\n",
+ ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ status = wait_for_flash_ready(FLASH_ERASE_CODE0);
+
+ if (status != OK) {
+ pr_err("flash_erase_page_by_page: ERROR %08X\n",
+ ERROR_FLASH_NOT_READY);
+ return status | ERROR_FLASH_NOT_READY;
+ /* Flash not ready within the chosen time, better exit! */
+ }
+
+ pr_info("Erase flash page by page DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Start the DMA procedure which actually transfer and burn the data loaded
+ * from memory into the Flash
+ * @return OK if success or an error code which specify the type of error
+ */
+int start_flash_dma(void)
+{
+ int status;
+ u8 cmd[12] = { FLASH_CMD_WRITE_REGISTER, 0x20, 0x00, 0x00,
+ 0x6B, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x00,
+ FLASH_DMA_CODE1 };
+
+ /* write the command to erase the flash */
+
+ pr_info("Command flash DMA ...\n");
+ if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) {
+ pr_err("start_flash_dma: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ status = wait_for_flash_ready(FLASH_DMA_CODE0);
+
+ if (status != OK) {
+ pr_err("start_flash_dma: ERROR %08X\n", ERROR_FLASH_NOT_READY);
+ return status | ERROR_FLASH_NOT_READY;
+ /* Flash not ready within the chosen time, better exit! */
+ }
+
+ pr_info("flash DMA DONE!\n");
+
+ return OK;
+}
+
+/**
+ * Copy the FW data that should be burnt in the Flash into the memory and then
+ * the DMA will take care about burning it into the Flash
+ * @param address address in memory where to copy the data, possible values
+ * are FLASH_ADDR_CODE, FLASH_ADDR_CONFIG, FLASH_ADDR_CX
+ * @param data pointer to an array of byte which contain the data that should
+ * be copied into the memory
+ * @param size size of data
+ * @return OK if success or an error code which specify the type of error
+ */
+int fillFlash(u32 address, u8 *data, int size)
+{
+ int remaining = size, index = 0;
+ int toWrite = 0;
+ int byteBlock = 0;
+ int wheel = 0;
+ u32 addr = 0;
+ int res;
+ int delta;
+ u8 *buff = NULL;
+
+ buff = kmalloc(max(DMA_CHUNK + 5, 12), GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("fillFlash: ERROR %08X\n", ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+
+ while (remaining > 0) {
+ byteBlock = 0;
+ addr = 0x00100000;
+
+ while (byteBlock < FLASH_CHUNK && remaining > 0) {
+ index = 0;
+ if (remaining >= DMA_CHUNK) {
+ if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) {
+ /* pr_err("fillFlash: 1\n"); */
+ toWrite = DMA_CHUNK;
+ remaining -= DMA_CHUNK;
+ byteBlock += DMA_CHUNK;
+ } else {
+ /* pr_err("fillFlash: 2\n); */
+ delta = FLASH_CHUNK - byteBlock;
+ toWrite = delta;
+ remaining -= delta;
+ byteBlock += delta;
+ }
+ } else {
+ if ((byteBlock + remaining) <= FLASH_CHUNK) {
+ /* pr_err("fillFlash: 3\n"); */
+ toWrite = remaining;
+ byteBlock += remaining;
+ remaining = 0;
+ } else {
+ /* pr_err("fillFlash: 4\n"); */
+ delta = FLASH_CHUNK - byteBlock;
+ toWrite = delta;
+ remaining -= delta;
+ byteBlock += delta;
+ }
+ }
+
+ buff[index++] = FTS_CMD_HW_REG_W;
+ buff[index++] = (u8)((addr & 0xFF000000) >> 24);
+ buff[index++] = (u8)((addr & 0x00FF0000) >> 16);
+ buff[index++] = (u8)((addr & 0x0000FF00) >> 8);
+ buff[index++] = (u8)(addr & 0x000000FF);
+
+ memcpy(&buff[index], data, toWrite);
+ /* pr_err("Command = %02X , address = %02X %02X
+ * , bytes = %d, data = %02X %02X, %02X %02X\n",
+ * buff[0], buff[1], buff[2], toWrite, buff[3],
+ * buff[4], buff[3 + toWrite-2],
+ * buff[3 + toWrite-1]); */
+ if (fts_write_heap(buff, index + toWrite) < OK) {
+ pr_err("fillFlash: ERROR %08X\n", ERROR_BUS_W);
+ kfree(buff);
+ return ERROR_BUS_W;
+ }
+
+ /* mdelay(5); */
+ addr += toWrite;
+ data += toWrite;
+ }
+
+
+ /* configuring the DMA */
+ byteBlock = byteBlock / 4 - 1;
+ index = 0;
+
+ buff[index++] = FLASH_CMD_WRITE_REGISTER;
+ buff[index++] = 0x20;
+ buff[index++] = 0x00;
+ buff[index++] = 0x00;
+ buff[index++] = FLASH_DMA_CONFIG;
+ buff[index++] = 0x00;
+ buff[index++] = 0x00;
+
+ addr = address + ((wheel * FLASH_CHUNK) / 4);
+ buff[index++] = (u8)((addr & 0x000000FF));
+ buff[index++] = (u8)((addr & 0x0000FF00) >> 8);
+ buff[index++] = (u8)(byteBlock & 0x000000FF);
+ buff[index++] = (u8)((byteBlock & 0x0000FF00) >> 8);
+ buff[index++] = 0x00;
+
+ pr_info("DMA Command = %02X , address = %02X %02X, words = %02X %02X\n",
+ buff[0], buff[8], buff[7], buff[10], buff[9]);
+
+ if (fts_write_heap(buff, index) < OK) {
+ pr_err("Error during filling Flash! ERROR %08X\n",
+ ERROR_BUS_W);
+ kfree(buff);
+ return ERROR_BUS_W;
+ }
+
+ res = start_flash_dma();
+ if (res < OK) {
+ pr_err("Error during flashing DMA! ERROR %08X\n", res);
+ kfree(buff);
+ return res;
+ }
+ wheel++;
+ }
+ kfree(buff);
+ return OK;
+}
+
+
+/*
+ * Execute the procedure to burn a FW on FTM5/FTI IC
+ *
+ * @param fw - structure which contain the FW to be burnt
+ * @param force_burn - if >0, the flashing procedure will be forced and
+ * executed
+ * regardless the additional info, otherwise the FW in the file will be
+ * burned only if it is different from the one running in the IC
+ * @param keep_cx - if 1, the function preserves the CX/Panel Init area.
+ * Otherwise, it will be cleared.
+ *
+ * @return OK if success or an error code which specifies the type of error
+ * encountered
+ */
+int flash_burn(Firmware fw, int force_burn, int keep_cx)
+{
+ int res;
+
+ if (!force_burn) {
+ /* Compare firmware, config, and CX versions */
+ if (fw.fw_ver != (uint32_t)systemInfo.u16_fwVer ||
+ fw.config_ver != (uint32_t)systemInfo.u16_cfgVer ||
+ fw.cx_ver != (uint32_t)systemInfo.u16_cxVer)
+ goto start;
+
+ for (res = EXTERNAL_RELEASE_INFO_SIZE - 1; res >= 0; res--) {
+ if (fw.externalRelease[res] !=
+ systemInfo.u8_releaseInfo[res])
+ goto start;
+ }
+
+ pr_info("flash_burn: Firmware in the chip matches the firmware to flash! NO UPDATE ERROR %08X\n",
+ ERROR_FW_NO_UPDATE);
+ return ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED;
+ } else if (force_burn == CRC_CX && fw.sec2_size == 0) {
+ /* burn procedure to update the CX memory, if not present just
+ * skip it!
+ */
+ for (res = EXTERNAL_RELEASE_INFO_SIZE - 1; res >= 0; res--) {
+ if (fw.externalRelease[res] !=
+ systemInfo.u8_releaseInfo[res]) {
+ /* Avoid loading the CX because it is missing
+ * in the bin file, it just need to update
+ * to last fw+cfg because a new release */
+ force_burn = 0;
+ goto start;
+ }
+ }
+ pr_info("flash_burn: CRC in CX but fw does not contain CX data! NO UPDATE ERROR %08X\n",
+ ERROR_FW_NO_UPDATE);
+ return ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED;
+ }
+
+ /* Programming procedure start */
+start:
+ pr_info("Programming Procedure for flashing started:\n\n");
+
+ pr_info(" 1) SYSTEM RESET:\n");
+ res = fts_system_reset();
+ if (res < 0) {
+ pr_err(" system reset FAILED!\n");
+ /* If there is no firmware, there is no controller ready event
+ * and there will be a timeout, we can keep going. But if
+ * there is an I2C error, we must exit.
+ */
+ if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT))
+ return res | ERROR_FLASH_BURN_FAILED;
+ } else
+ pr_info(" system reset COMPLETED!\n\n");
+
+ msleep(100); /* required by HW for safe flash procedure */
+
+ pr_info(" 2) HOLD M3 :\n");
+
+ res = hold_m3();
+ if (res < OK) {
+ pr_err(" hold_m3 FAILED!\n");
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" hold_m3 COMPLETED!\n\n");
+
+ pr_info("3) ENABLE UVLO AND AUTO POWER DOWN MODE :\n");
+ res = flash_enable_uvlo_autopowerdown();
+ if (res < OK) {
+ pr_err(" flash_enable_uvlo_autopowerdown FAILED!\n");
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_err(" flash_enable_uvlo_autopowerdown COMPLETED!\n\n");
+
+ pr_info(" 4) FLASH UNLOCK:\n");
+ res = flash_unlock();
+ if (res < OK) {
+ pr_err(" flash unlock FAILED! ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" flash unlock COMPLETED!\n\n");
+
+ pr_info(" 5) FLASH ERASE UNLOCK:\n");
+ res = flash_erase_unlock();
+ if (res < 0) {
+ pr_err(" flash unlock FAILED! ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" flash unlock COMPLETED!\n\n");
+
+ pr_info(" 6) FLASH ERASE:\n");
+ if (keep_cx > 0) {
+ if (fw.sec2_size != 0 && force_burn == CRC_CX)
+ res = flash_erase_page_by_page(SKIP_PANEL_INIT, &fw);
+ else
+ res = flash_erase_page_by_page(SKIP_PANEL_CX_INIT, &fw);
+ } else {
+ res = flash_erase_page_by_page(SKIP_PANEL_INIT, &fw);
+ if (fw.sec2_size == 0)
+ pr_err("WARNING!!! Erasing CX memory but no CX in fw file! touch will not work right after fw update!\n");
+ }
+
+ if (res < OK) {
+ pr_err(" flash erase FAILED! ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" flash erase COMPLETED!\n\n");
+
+ pr_info(" 7) LOAD PROGRAM:\n");
+ res = fillFlash(fw.code_start_addr, &fw.data[0], fw.sec0_size);
+ if (res < OK) {
+ pr_err(" load program ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" load program DONE!\n");
+
+ pr_info(" 8) LOAD CONFIG:\n");
+ res = fillFlash(fw.config_start_addr, &(fw.data[fw.sec0_size]),
+ fw.sec1_size);
+ if (res < OK) {
+ pr_err(" load config ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" load config DONE!\n");
+
+ if (fw.sec2_size != 0 && (force_burn == CRC_CX || keep_cx <= 0)) {
+ pr_info(" 8.1) LOAD CX:\n");
+ res = fillFlash(fw.cx_start_addr,
+ &(fw.data[fw.sec0_size + fw.sec1_size]),
+ fw.sec2_size);
+ if (res < OK) {
+ pr_err(" load cx ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" load cx DONE!\n");
+ }
+
+ pr_info(" Flash burn COMPLETED!\n\n");
+
+ pr_info(" 9) SYSTEM RESET:\n");
+ res = fts_system_reset();
+ if (res < 0) {
+ pr_err(" system reset FAILED! ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+ pr_info(" system reset COMPLETED!\n\n");
+
+ pr_info(" 10) FINAL CHECK:\n");
+ res = readSysInfo(0);
+ if (res < 0) {
+ pr_err("flash_burn: Unable to retrieve Chip INFO! ERROR %08X\n",
+ ERROR_FLASH_BURN_FAILED);
+ return res | ERROR_FLASH_BURN_FAILED;
+ }
+
+ for (res = 0; res < EXTERNAL_RELEASE_INFO_SIZE; res++) {
+ if (fw.externalRelease[res] != systemInfo.u8_releaseInfo[res]) {
+ /* External release is printed during readSysInfo */
+ pr_info(" Firmware in the chip different from the one that was burn!\n");
+ return ERROR_FLASH_BURN_FAILED;
+ }
+ }
+
+ pr_info(" Final check OK!\n");
+
+ return OK;
+}
diff --git a/fts_lib/ftsFlash.h b/fts_lib/ftsFlash.h
new file mode 100644
index 0000000..2a52c53
--- /dev/null
+++ b/fts_lib/ftsFlash.h
@@ -0,0 +1,140 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS API for Flashing the IC *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsFlash.h
+ * \brief Contains all the definitions and structs to handle the FW update
+ *process
+ */
+
+#ifndef FTS_FLASH_H
+#define FTS_FLASH_H
+
+#include "ftsSoftware.h"
+
+/* Flash possible status */
+#define FLASH_READY 0 /* /< value to indicate that the flash
+ * is ready */
+#define FLASH_BUSY 1 /* /< value to indicate that the flash
+ * is busy */
+#define FLASH_UNKNOWN -1 /* /< value to indicate an unknown
+ * status of the flash */
+
+#define FLASH_STATUS_BYTES 1 /* /< number of bytes to check for read
+ * the flash status */
+
+
+
+/* Flash timing parameters */
+#define FLASH_RETRY_COUNT 200 /* /< number of attempts to read the
+ * flash status */
+#define FLASH_WAIT_BEFORE_RETRY 50 /* /< time to wait in ms between status
+ * readings */
+
+
+#ifdef FW_H_FILE
+#define PATH_FILE_FW "NULL"
+#else
+#define PATH_FILE_FW "ftm5_fw.ftb" /* new FW bin file name */
+#endif
+
+#ifdef ALIX
+#define FLASH_CHUNK (32 * 1024) /* /< Max number of bytes that
+ * the
+ * DMA can burn on flash in
+ *one shot in FTI */
+#else
+#define FLASH_CHUNK (64 * 1024) /* /< Max number of bytes that
+ * the
+ * DMA can burn on flash in
+ *one shot in FTI */
+#endif
+
+#define DMA_CHUNK 32 /* /< Max number of bytes that can be
+ * written in I2C to the DMA */
+
+#define FLASH_ORG_INFO_INDEX 280
+
+#define FLASH_PAGE_SIZE (4 * 1024) //page size of 4KB
+
+/**
+ * Define which kind of erase page by page should be performed
+ */
+typedef enum {
+ ERASE_ALL = 0, /* /< erase all the pages */
+ SKIP_PANEL_INIT = 1, /* /< skip erase Panel Init Pages */
+ SKIP_PANEL_CX_INIT = 2 /* /< skip erase Panel Init and CX Pages */
+} ErasePage;
+
+/** @addtogroup fw_file
+ * @{
+ */
+
+/**
+ * Struct which contains information and data of the FW that should be burnt
+ *into the IC
+ */
+typedef struct {
+ u8 *data; /* /< pointer to an array of bytes which represent the
+ * FW data */
+ u16 fw_ver; /* /< FW version of the FW file */
+ u16 config_ver; /* Config version of the FW file */
+ u16 cx_ver; /* /< Cx version of the FW file */
+ u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; /* /< External Release
+ * Info of the FW file
+ * */
+ int data_size; /* /< dimension of data (the actual data to be burnt) */
+ u32 sec0_size; /* /< dimension of section 0 (FW) in .ftb file */
+ u32 sec1_size; /* /< dimension of section 1 (Config) in .ftb file */
+ u32 sec2_size; /* /< dimension of section 2 (Cx) in .ftb file */
+ u32 sec3_size; /* /< dimension of section 3 (TBD) in .ftb file */
+ u8 fw_code_size; /* /< size of fw code in pages in
+ .ftb file */
+ u8 panel_config_size;/* /< size of panel area in pages in
+ .ftb file */
+ u8 cx_area_size;/* /< size of cx area in pages in
+ .ftb file */
+ u8 fw_config_size;/* /< size of fw config in pages in
+ .ftb file */
+ u32 code_start_addr; /* start addr for fw code */
+ u32 cx_start_addr; /* start addr for cx area */
+ u32 config_start_addr; /* start addr for config area */
+} Firmware;
+
+/** @}*/
+
+/** @addtogroup flash_command
+ * @{
+ */
+
+int wait_for_flash_ready(u8 type);
+int hold_m3(void);
+int flash_erase_unlock(void);
+int flash_full_erase(void);
+int flash_erase_page_by_page(ErasePage keep_cx, Firmware *fw);
+int start_flash_dma(void);
+int fillFlash(u32 address, u8 *data, int size);
+
+int flash_unlock(void);
+int getFWdata(const char *pathToFile, u8 **data, int *size);
+int parseBinFile(u8 *fw_data, int fw_size, Firmware *fw, int keep_cx);
+int readFwFile(const char *path, Firmware *fw, int keep_cx);
+int flash_burn(Firmware fw, int force_burn, int keep_cx);
+int flashProcedure(const char *path, int force, int keep_cx);
+int flash_enable_uvlo_autopowerdown(void);
+
+#endif
+
+/** @}*/
diff --git a/fts_lib/ftsFrame.c b/fts_lib/ftsFrame.c
new file mode 100644
index 0000000..b79f498
--- /dev/null
+++ b/fts_lib/ftsFrame.c
@@ -0,0 +1,667 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS functions for getting frames *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsFrame.c
+ * \brief Contains all the functions to work with frames
+ */
+
+#include "ftsCompensation.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsFrame.h"
+#include "ftsHardware.h"
+#include "ftsIO.h"
+#include "ftsSoftware.h"
+#include "ftsTool.h"
+#include "ftsTime.h"
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/serio.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+
+
+
+
+extern SysInfo systemInfo; /* /< forward declaration of the global variable
+ * of containing System Info Data */
+
+
+/**
+ * Read the channels lengths from the config memory
+ * @return OK if success or an error code which specify the type of error
+ */
+int getChannelsLength(void)
+{
+ int ret;
+ u8 data[2];
+
+ if (data == NULL) {
+ pr_err("getChannelsLength: ERROR %08X\n", ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+
+ ret = readConfig(ADDR_CONFIG_SENSE_LEN, data, 2);
+ if (ret < OK) {
+ pr_err("getChannelsLength: ERROR %08X\n", ret);
+
+ return ret;
+ }
+
+ systemInfo.u8_scrRxLen = (int)data[0];
+ systemInfo.u8_scrTxLen = (int)data[1];
+
+ pr_info("Force_len = %d Sense_Len = %d\n",
+ systemInfo.u8_scrTxLen, systemInfo.u8_scrRxLen);
+
+ return OK;
+}
+
+
+
+/**
+ * Read and pack the frame data related to the nodes
+ * @param address address in memory when the frame data node start
+ * @param size amount of data to read
+ * @param frame pointer to an array of bytes which will contain the frame node
+ * data
+ * @return OK if success or an error code which specify the type of error
+ */
+int getFrameData(u16 address, int size, short *frame)
+{
+ int i, j, ret;
+ u8 *data = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
+
+ if (data == NULL) {
+ pr_err("getFrameData: ERROR %08X\n", ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data,
+ size, DUMMY_FRAMEBUFFER);
+ if (ret < OK) {
+ pr_err("getFrameData: ERROR %08X\n", ERROR_BUS_R);
+ kfree(data);
+ return ERROR_BUS_R;
+ }
+ j = 0;
+ for (i = 0; i < size; i += 2) {
+ frame[j] = (short)((data[i + 1] << 8) + data[i]);
+ j++;
+ }
+ kfree(data);
+ return OK;
+}
+
+
+/**
+ * Return the number of Sense Channels (Rx)
+ * @return number of Rx channels
+ */
+int getSenseLen(void)
+{
+ if (systemInfo.u8_scrRxLen == 0)
+ getChannelsLength();
+ return systemInfo.u8_scrRxLen;
+}
+
+/**
+ * Return the number of Force Channels (Tx)
+ * @return number of Tx channels
+ */
+int getForceLen(void)
+{
+ if (systemInfo.u8_scrTxLen == 0)
+ getChannelsLength();
+ return systemInfo.u8_scrTxLen;
+}
+
+
+/******************** New API **************************/
+
+/**
+ * Read a MS Frame from frame buffer memory
+ * @param type type of MS frame to read
+ * @param frame pointer to MutualSenseFrame variable which will contain the
+ * data
+ * @return > 0 if success specifying the number of node into the frame or
+ * an error code which specify the type of error
+ */
+int getMSFrame3(MSFrameType type, MutualSenseFrame *frame)
+{
+ u16 offset;
+ int ret, force_len, sense_len;
+
+ force_len = getForceLen();
+ sense_len = getSenseLen();
+
+ frame->node_data = NULL;
+
+ pr_debug("%s: Starting to get frame %02X\n", __func__,
+ type);
+ switch (type) {
+ case MS_RAW:
+ offset = systemInfo.u16_msTchRawAddr;
+ goto LOAD_NORM;
+ case MS_FILTER:
+ offset = systemInfo.u16_msTchFilterAddr;
+
+ goto LOAD_NORM;
+ case MS_STRENGTH:
+ offset = systemInfo.u16_msTchStrenAddr;
+ goto LOAD_NORM;
+ case MS_BASELINE:
+ offset = systemInfo.u16_msTchBaselineAddr;
+LOAD_NORM:
+ if (force_len == 0 || sense_len == 0) {
+ pr_err("%s: number of channels not initialized ERROR %08X\n",
+ __func__, ERROR_CH_LEN);
+ return ERROR_CH_LEN | ERROR_GET_FRAME;
+ }
+
+ break;
+
+ case MS_KEY_RAW:
+ offset = systemInfo.u16_keyRawAddr;
+ goto LOAD_KEY;
+ case MS_KEY_FILTER:
+ offset = systemInfo.u16_keyFilterAddr;
+ goto LOAD_KEY;
+ case MS_KEY_STRENGTH:
+ offset = systemInfo.u16_keyStrenAddr;
+ goto LOAD_KEY;
+ case MS_KEY_BASELINE:
+ offset = systemInfo.u16_keyBaselineAddr;
+LOAD_KEY:
+ if (systemInfo.u8_keyLen == 0) {
+ pr_err("%s: number of channels not initialized ERROR %08X\n",
+ __func__, ERROR_CH_LEN);
+ return ERROR_CH_LEN | ERROR_GET_FRAME;
+ }
+ force_len = 1;
+ sense_len = systemInfo.u8_keyLen;
+ break;
+
+ case FRC_RAW:
+ offset = systemInfo.u16_frcRawAddr;
+ goto LOAD_FRC;
+ case FRC_FILTER:
+ offset = systemInfo.u16_frcFilterAddr;
+ goto LOAD_FRC;
+ case FRC_STRENGTH:
+ offset = systemInfo.u16_frcStrenAddr;
+ goto LOAD_FRC;
+ case FRC_BASELINE:
+ offset = systemInfo.u16_frcBaselineAddr;
+LOAD_FRC:
+ if (force_len == 0) {
+ pr_err("%s: number of channels not initialized ERROR %08X\n",
+ __func__, ERROR_CH_LEN);
+ return ERROR_CH_LEN | ERROR_GET_FRAME;
+ }
+ sense_len = 1;
+ break;
+ default:
+ pr_err("%s: Invalid type ERROR %08X\n", __func__,
+ ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME);
+ return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME;
+ }
+
+ frame->node_data_size = ((force_len) * sense_len);
+ frame->header.force_node = force_len;
+ frame->header.sense_node = sense_len;
+ frame->header.type = type;
+
+ pr_debug("%s: Force_len = %d Sense_len = %d Offset = %04X\n",
+ __func__, force_len, sense_len, offset);
+
+ frame->node_data = (short *)kmalloc(frame->node_data_size *
+ sizeof(short), GFP_KERNEL);
+ if (frame->node_data == NULL) {
+ pr_err("%s: ERROR %08X\n", __func__,
+ ERROR_ALLOC | ERROR_GET_FRAME);
+ return ERROR_ALLOC | ERROR_GET_FRAME;
+ }
+
+ ret = getFrameData(offset, frame->node_data_size * BYTES_PER_NODE,
+ (frame->node_data));
+ if (ret < OK) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_GET_FRAME_DATA);
+ kfree(frame->node_data);
+ frame->node_data = NULL;
+ return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ }
+ /* if you want to access one node i,j,
+ * compute the offset like: offset = i*columns + j = > frame[i, j] */
+
+ pr_debug("Frame acquired!\n");
+ return frame->node_data_size;
+ /* return the number of data put inside frame */
+}
+
+/**
+ * Read a SS Frame from frame buffer
+ * @param type type of SS frame to read
+ * @param frame pointer to SelfSenseFrame variable which will contain the data
+ * @return > 0 if success specifying the number of node into frame or an
+ * error code which specify the type of error
+ */
+int getSSFrame3(SSFrameType type, SelfSenseFrame *frame)
+{
+ u16 offset_force, offset_sense;
+ int ret;
+
+ frame->force_data = NULL;
+ frame->sense_data = NULL;
+
+ frame->header.force_node = getForceLen(); /* use getForce/SenseLen
+ * because introduce
+ * a recover mechanism
+ * in case of len =0 */
+ frame->header.sense_node = getSenseLen();
+
+ if (frame->header.force_node == 0 || frame->header.sense_node == 0) {
+ pr_err("%s: number of channels not initialized ERROR %08X\n",
+ __func__, ERROR_CH_LEN);
+ return ERROR_CH_LEN | ERROR_GET_FRAME;
+ }
+
+
+ pr_info("%s: Starting to get frame %02X\n", __func__, type);
+ switch (type) {
+ case SS_RAW:
+ offset_force = systemInfo.u16_ssTchTxRawAddr;
+ offset_sense = systemInfo.u16_ssTchRxRawAddr;
+ break;
+ case SS_FILTER:
+ offset_force = systemInfo.u16_ssTchTxFilterAddr;
+ offset_sense = systemInfo.u16_ssTchRxFilterAddr;
+ break;
+ case SS_STRENGTH:
+ offset_force = systemInfo.u16_ssTchTxStrenAddr;
+ offset_sense = systemInfo.u16_ssTchRxStrenAddr;
+ break;
+ case SS_BASELINE:
+ offset_force = systemInfo.u16_ssTchTxBaselineAddr;
+ offset_sense = systemInfo.u16_ssTchRxBaselineAddr;
+ break;
+
+ case SS_HVR_RAW:
+ offset_force = systemInfo.u16_ssHvrTxRawAddr;
+ offset_sense = systemInfo.u16_ssHvrRxRawAddr;
+ break;
+ case SS_HVR_FILTER:
+ offset_force = systemInfo.u16_ssHvrTxFilterAddr;
+ offset_sense = systemInfo.u16_ssHvrRxFilterAddr;
+ break;
+ case SS_HVR_STRENGTH:
+ offset_force = systemInfo.u16_ssHvrTxStrenAddr;
+ offset_sense = systemInfo.u16_ssHvrRxStrenAddr;
+ break;
+ case SS_HVR_BASELINE:
+ offset_force = systemInfo.u16_ssHvrTxBaselineAddr;
+ offset_sense = systemInfo.u16_ssHvrRxBaselineAddr;
+ break;
+
+ case SS_PRX_RAW:
+ offset_force = systemInfo.u16_ssPrxTxRawAddr;
+ offset_sense = systemInfo.u16_ssPrxRxRawAddr;
+ break;
+ case SS_PRX_FILTER:
+ offset_force = systemInfo.u16_ssPrxTxFilterAddr;
+ offset_sense = systemInfo.u16_ssPrxRxFilterAddr;
+ break;
+ case SS_PRX_STRENGTH:
+ offset_force = systemInfo.u16_ssPrxTxStrenAddr;
+ offset_sense = systemInfo.u16_ssPrxRxStrenAddr;
+ break;
+ case SS_PRX_BASELINE:
+ offset_force = systemInfo.u16_ssPrxTxBaselineAddr;
+ offset_sense = systemInfo.u16_ssPrxRxBaselineAddr;
+ break;
+ case SS_DETECT_RAW:
+ if (systemInfo.u8_ssDetScanSet == 0) {
+ offset_force = systemInfo.u16_ssDetRawAddr;
+ offset_sense = 0;
+ frame->header.sense_node = 0;
+ } else {
+ offset_sense = systemInfo.u16_ssDetRawAddr;
+ offset_force = 0;
+ frame->header.force_node = 0;
+ }
+ break;
+
+ case SS_DETECT_FILTER:
+ if (systemInfo.u8_ssDetScanSet == 0) {
+ offset_force = systemInfo.u16_ssDetFilterAddr;
+ offset_sense = 0;
+ frame->header.sense_node = 0;
+ } else {
+ offset_sense = systemInfo.u16_ssDetFilterAddr;
+ offset_force = 0;
+ frame->header.force_node = 0;
+ }
+ break;
+
+ case SS_DETECT_BASELINE:
+ if (systemInfo.u8_ssDetScanSet == 0) {
+ offset_force = systemInfo.u16_ssDetBaselineAddr;
+ offset_sense = 0;
+ frame->header.sense_node = 0;
+ } else {
+ offset_sense = systemInfo.u16_ssDetBaselineAddr;
+ offset_force = 0;
+ frame->header.force_node = 0;
+ }
+ break;
+
+ case SS_DETECT_STRENGTH:
+ if (systemInfo.u8_ssDetScanSet == 0) {
+ offset_force = systemInfo.u16_ssDetStrenAddr;
+ offset_sense = 0;
+ frame->header.sense_node = 0;
+ } else {
+ offset_sense = systemInfo.u16_ssDetStrenAddr;
+ offset_force = 0;
+ frame->header.force_node = 0;
+ }
+ break;
+
+ default:
+ pr_err("%s: Invalid type ERROR %08X\n", __func__,
+ ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME);
+ return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME;
+ }
+
+ frame->header.type = type;
+
+ pr_info("%s: Force_len = %d Sense_len = %d Offset_force = %04X Offset_sense = %04X\n",
+ __func__, frame->header.force_node,
+ frame->header.sense_node,
+ offset_force, offset_sense);
+
+ frame->force_data = (short *)kmalloc(frame->header.force_node *
+ sizeof(short), GFP_KERNEL);
+ if (frame->force_data == NULL) {
+ pr_err("%s: can not allocate force_data ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ return ERROR_ALLOC | ERROR_GET_FRAME;
+ }
+
+ frame->sense_data = (short *)kmalloc(frame->header.sense_node *
+ sizeof(short), GFP_KERNEL);
+ if (frame->sense_data == NULL) {
+ kfree(frame->force_data);
+ frame->force_data = NULL;
+ pr_err("%s: can not allocate sense_data ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ return ERROR_ALLOC | ERROR_GET_FRAME;
+ }
+
+ ret = getFrameData(offset_force, frame->header.force_node *
+ BYTES_PER_NODE, (frame->force_data));
+ if (ret < OK) {
+ pr_err("%s: error while reading force data ERROR %08X\n",
+ __func__, ERROR_GET_FRAME_DATA);
+ kfree(frame->force_data);
+ frame->force_data = NULL;
+ kfree(frame->sense_data);
+ frame->sense_data = NULL;
+ return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ }
+
+ ret = getFrameData(offset_sense, frame->header.sense_node *
+ BYTES_PER_NODE, (frame->sense_data));
+ if (ret < OK) {
+ pr_err("%s: error while reading sense data ERROR %08X\n",
+ __func__, ERROR_GET_FRAME_DATA);
+ kfree(frame->force_data);
+ frame->force_data = NULL;
+ kfree(frame->sense_data);
+ frame->sense_data = NULL;
+ return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ }
+ /* if you want to access one node i,j,
+ * the offset like: offset = i*columns + j = > frame[i, j] */
+
+ pr_info("Frame acquired!\n");
+ return frame->header.force_node + frame->header.sense_node;
+ /* return the number of data put inside frame */
+}
+
+
+/**
+ * Read Initialization Data Header and check that the type loaded match with
+ * the one previously requested
+ * @param type type of Initialization data requested @link load_opt Load Host
+ * Data Option @endlink
+ * @param msHeader pointer to DataHeader variable which will contain the header
+ * info for the MS frame
+ * @param ssHeader pointer to DataHeader variable which will contain the header
+ * info for the SS frame
+ * @param address pointer to a variable which will contain the updated address
+ * to the next data
+ * @return OK if success or an error code which specify the type of error
+ */
+int readSyncDataHeader(u8 type, DataHeader *msHeader, DataHeader *ssHeader,
+ u64 *address)
+{
+ u64 offset = ADDR_FRAMEBUFFER;
+ u8 data[SYNCFRAME_DATA_HEADER];
+ int ret;
+
+ ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, offset, data,
+ SYNCFRAME_DATA_HEADER, DUMMY_FRAMEBUFFER);
+ if (ret < OK) { /* i2c function have already a retry mechanism */
+ pr_err("%s: error while reading data header ERROR %08X\n",
+ __func__, ret);
+ return ret;
+ }
+
+ pr_info("Read Data Header done!\n");
+
+ if (data[0] != HEADER_SIGNATURE) {
+ pr_err("%s: The Header Signature was wrong! %02X != %02X ERROR %08X\n",
+ __func__, data[0], HEADER_SIGNATURE,
+ ERROR_WRONG_DATA_SIGN);
+ return ERROR_WRONG_DATA_SIGN;
+ }
+
+
+ if (data[1] != type) {
+ pr_err("%s: Wrong type found! %02X!=%02X ERROR %08X\n",
+ __func__, data[1], type, ERROR_DIFF_DATA_TYPE);
+ return ERROR_DIFF_DATA_TYPE;
+ }
+
+ pr_info("Type = %02X of SyncFrame data OK!\n", type);
+
+ msHeader->force_node = data[5];
+ msHeader->sense_node = data[6];
+ pr_info("MS Frame force_node = %d, sense_node = %d\n",
+ msHeader->force_node, msHeader->sense_node);
+
+ ssHeader->force_node = data[7];
+ ssHeader->sense_node = data[8];
+ pr_info("SS Frame force_node = %d, sense_node = %d\n",
+ ssHeader->force_node, ssHeader->sense_node);
+
+ *address = offset + SYNCFRAME_DATA_HEADER + data[4];
+
+ return OK;
+}
+
+/**
+ * Read a Sync Frame from frame buffer which contain MS and SS data collected
+ * for the same scan
+ * @param type type of Sync frame to read, possible values:
+ * LOAD_SYNC_FRAME_RAW, LOAD_SYNC_FRAME_FILTER, LOAD_SYNC_FRAME_BASELINE,
+ * LOAD_SYNC_FRAME_STRENGTH
+ * @param msFrame pointer to MutualSenseFrame variable which will contain the
+ * MS data
+ * @param ssFrame pointer to SelfSenseFrame variable which will contain the SS
+ * data
+ * @return >0 if success specifying the total number of nodes copied into
+ * msFrame and ssFrame or an error code which specify the type of error
+ */
+int getSyncFrame(u8 type, MutualSenseFrame *msFrame, SelfSenseFrame *ssFrame)
+{
+ int res;
+ u64 address;
+
+ msFrame->node_data = NULL;
+ ssFrame->force_data = NULL;
+ ssFrame->sense_data = NULL;
+
+ pr_info("%s: Starting to get Sync Frame %02X...\n", __func__, type);
+ switch (type) {
+ case LOAD_SYNC_FRAME_RAW:
+ msFrame->header.type = MS_RAW;
+ ssFrame->header.type = SS_RAW;
+ break;
+
+ case LOAD_SYNC_FRAME_FILTER:
+ msFrame->header.type = MS_FILTER;
+ ssFrame->header.type = SS_FILTER;
+ break;
+
+ case LOAD_SYNC_FRAME_BASELINE:
+ msFrame->header.type = MS_BASELINE;
+ ssFrame->header.type = SS_BASELINE;
+ break;
+
+ case LOAD_SYNC_FRAME_STRENGTH:
+ msFrame->header.type = MS_STRENGTH;
+ ssFrame->header.type = SS_STRENGTH;
+ break;
+
+ default:
+ return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME;
+ }
+
+ pr_info("%s: Requesting Sync Frame %02X...\n", __func__, type);
+ res = requestSyncFrame(type);
+ if (res < OK) {
+ pr_err("%s: error while requesting Sync Frame ERROR %08X\n",
+ __func__, res | ERROR_GET_FRAME_DATA);
+ return res | ERROR_GET_FRAME_DATA;
+ }
+
+ res = readSyncDataHeader(type, &(msFrame->header), &(ssFrame->header),
+ &address);
+ if (res < OK) {
+ pr_err("%s: error while reading Sync Frame header... ERROR %08X\n",
+ __func__, res | ERROR_GET_FRAME_DATA);
+ return res | ERROR_GET_FRAME_DATA;
+ }
+
+ msFrame->node_data_size = msFrame->header.force_node *
+ msFrame->header.sense_node;
+
+ msFrame->node_data = (short *)kmalloc(msFrame->node_data_size *
+ sizeof(short), GFP_KERNEL);
+ if (msFrame->node_data == NULL) {
+ pr_err("%s: impossible allocate memory for MS frame... ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ return ERROR_ALLOC | ERROR_GET_FRAME;
+ }
+
+ pr_info("%s: Getting MS frame at %llx...\n", __func__, address);
+ res = getFrameData(address, (msFrame->node_data_size) * BYTES_PER_NODE,
+ (msFrame->node_data));
+ if (res < OK) {
+ pr_err("%s: error while getting MS data...ERROR %08X\n",
+ __func__, res);
+ res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* move the offset */
+ address += (msFrame->node_data_size) * BYTES_PER_NODE;
+
+ ssFrame->force_data = (short *)kmalloc(ssFrame->header.force_node *
+ sizeof(short), GFP_KERNEL);
+ if (ssFrame->force_data == NULL) {
+ pr_err("%s: impossible allocate memory for SS force frame...ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ res = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ pr_info("%s: Getting SS force frame at %llx...\n", __func__, address);
+ res = getFrameData(address, (ssFrame->header.force_node) *
+ BYTES_PER_NODE, (ssFrame->force_data));
+ if (res < OK) {
+ pr_err("%s: error while getting SS force data...ERROR %08X\n",
+ __func__, res);
+ res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* move the offset */
+ address += (ssFrame->header.force_node) * BYTES_PER_NODE;
+
+ ssFrame->sense_data = (short *)kmalloc(ssFrame->header.sense_node *
+ sizeof(short), GFP_KERNEL);
+ if (ssFrame->sense_data == NULL) {
+ pr_err("%s: impossible allocate memory for SS sense frame...ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ res = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ pr_info("%s: Getting SS sense frame at %llx...\n", __func__, address);
+ res = getFrameData(address, (ssFrame->header.sense_node) *
+ BYTES_PER_NODE, (ssFrame->sense_data));
+ if (res < OK) {
+ pr_err("%s: error while getting SS sense data...ERROR %08X\n",
+ __func__, res);
+ res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ERROR:
+ if (res < OK) {
+ if (msFrame->node_data != NULL) {
+ kfree(msFrame->node_data);
+ msFrame->node_data = NULL;
+ }
+
+ if (ssFrame->force_data != NULL) {
+ kfree(ssFrame->force_data);
+ ssFrame->force_data = NULL;
+ }
+
+ if (ssFrame->sense_data != NULL) {
+ kfree(ssFrame->sense_data);
+ ssFrame->sense_data = NULL;
+ }
+ pr_err("Getting Sync Frame FAILED! ERROR %08X!\n", res);
+ } else {
+ pr_info("Getting Sync Frame FINISHED!\n");
+ res = msFrame->node_data_size + ssFrame->header.force_node +
+ ssFrame->header.sense_node;
+ }
+ return res;
+}
diff --git a/fts_lib/ftsFrame.h b/fts_lib/ftsFrame.h
new file mode 100644
index 0000000..a2e516d
--- /dev/null
+++ b/fts_lib/ftsFrame.h
@@ -0,0 +1,118 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS functions for getting frames *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsFrame.h
+ * \brief Contains all the definitions and structs to work with frames
+ */
+
+
+#ifndef FTS_FRAME_H
+#define FTS_FRAME_H
+
+#include "ftsSoftware.h"
+#include "ftsCore.h"
+
+
+
+/* Number of data bytes for each node */
+#define BYTES_PER_NODE 2 /* /< number of data bytes for each node
+ * */
+
+
+#define RETRY_FRAME_DATA_READ 2 /* /< max number of attempts to read a
+ * frame */
+#define SYNCFRAME_DATA_HEADER (DATA_HEADER + 12) /* /< number of bytes of
+ * Sync Frame Header */
+
+/**
+ * Possible types of MS frames
+ */
+typedef enum {
+ MS_RAW = 0, /* /< Mutual Sense Raw Frame */
+ MS_FILTER = 1, /* /< Mutual Sense Filtered Frame */
+ MS_STRENGTH = 2, /* /< Mutual Sense Strength Frame (Baseline-Raw)
+ * */
+ MS_BASELINE = 3, /* /< Mutual Sense Baseline Frame */
+ MS_KEY_RAW = 4, /* /< Mutual Sense Key Raw Frame */
+ MS_KEY_FILTER = 5, /* /< Mutual Sense Key Filter Frame */
+ MS_KEY_STRENGTH = 6, /* /< Mutual Sense Key Strength Frame
+ * (Baseline-Raw) */
+ MS_KEY_BASELINE = 7, /* /< Mutual Sense Key Baseline Frame */
+ FRC_RAW = 8, /* /< Force Raw Frame */
+ FRC_FILTER = 9, /* /< Force Filtered Frame */
+ FRC_STRENGTH = 10, /* /< Force Strength Frame (Baseline-Raw) */
+ FRC_BASELINE = 11 /* /< Force Baseline Frame */
+} MSFrameType;
+
+
+/**
+ * Possible types of SS frames
+ */
+typedef enum {
+ SS_RAW = 0, /* /< Self Sense Raw Frame */
+ SS_FILTER = 1, /* /< Self Sense Filtered Frame */
+ SS_STRENGTH = 2, /* /< Self Sense Strength Frame (Baseline-Raw)
+ * */
+ SS_BASELINE = 3, /* /< Self Sense Baseline Frame */
+ SS_HVR_RAW = 4, /* /< Self Sense Hover Raw Frame */
+ SS_HVR_FILTER = 5, /* /< Self Sense Hover Filter Frame */
+ SS_HVR_STRENGTH = 6, /* /< Self Sense Hover Strength Frame
+ * (Baseline-Raw) */
+ SS_HVR_BASELINE = 7, /* /< Self Sense Hover Baseline Frame */
+ SS_PRX_RAW = 8, /* /< Self Sense Proximity Raw Frame */
+ SS_PRX_FILTER = 9, /* /< Self Sense Proximity Filtered Frame */
+ SS_PRX_STRENGTH = 10, /* /< Self Sense Proximity Strength Frame
+ * (Baseline-Raw) */
+ SS_PRX_BASELINE = 11, /* /< Self Sense Proximity Baseline Frame */
+
+ SS_DETECT_RAW = 12, /* /< Self Sense Detect Raw Frame */
+ SS_DETECT_FILTER = 13, /* /< Self Sense Detect Filter Frame */
+ SS_DETECT_STRENGTH = 14, /* /< Self Sense Detect Strength Frame */
+ SS_DETECT_BASELINE = 15 /* /< Self Sense Detect Baseline Frame */
+} SSFrameType;
+
+
+/**
+ * Struct which contains the data of a MS Frame
+ */
+typedef struct {
+ DataHeader header; /* /< Header which contain basic info of the
+ * frame */
+ short *node_data; /* /< Data of the frame */
+ int node_data_size; /* /< Dimension of the data of the frame */
+} MutualSenseFrame;
+
+/**
+ * Struct which contains the data of a SS Frame
+ */
+typedef struct {
+ DataHeader header; /* /< Header which contain basic info of the
+ * frame */
+ short *force_data; /* /< Force Channels Data */
+ short *sense_data; /* /< Sense Channels Data */
+} SelfSenseFrame;
+
+
+int getChannelsLength(void);
+int getFrameData(u16 address, int size, short *frame);
+int getSenseLen(void);
+int getForceLen(void);
+int getMSFrame3(MSFrameType type, MutualSenseFrame *frame);
+int getSSFrame3(SSFrameType type, SelfSenseFrame *frame);
+int readSyncDataHeader(u8 type, DataHeader *msHeader, DataHeader *ssHeader,
+ u64 *address);
+int getSyncFrame(u8 type, MutualSenseFrame *msFrame, SelfSenseFrame *ssFrame);
+#endif
diff --git a/fts_lib/ftsGesture.c b/fts_lib/ftsGesture.c
new file mode 100644
index 0000000..2810d42
--- /dev/null
+++ b/fts_lib/ftsGesture.c
@@ -0,0 +1,377 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Gesture Utilities **
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsGesture.c
+ * \brief Contains all the functions and variable to handle the Gesture
+ * Detection features
+ */
+
+#include "ftsSoftware.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsGesture.h"
+#include "ftsIO.h"
+#include "ftsTime.h"
+#include "ftsTool.h"
+
+/* /< store the gesture bitmask which the host want to enable.
+ * If bit set 1 the corresponding gesture will be detected in Gesture Mode */
+static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 };
+/* /< store the x coordinates of the points draw by the user
+ * when a gesture is detected */
+u16 gesture_coordinates_x[GESTURE_MAX_COORDS_PAIRS_REPORT] = { 0 };
+/* /< store the y coordinates of the points draw by the user
+ * when a gesture is detected */
+u16 gesture_coordinates_y[GESTURE_MAX_COORDS_PAIRS_REPORT] = { 0 };
+/* /< number of coordinates pairs (points) reported with the detected gesture */
+int gesture_coords_reported = ERROR_OP_NOT_ALLOW;
+static u8 refreshGestureMask; /* /< flag which indicate if there is
+ * the need to set the gesture mask in the FW */
+struct mutex gestureMask_mutex; /* /< mutex used to control access on gesture
+ * shared variables */
+
+/**
+ * Update the gesture mask stored in the driver and have to be used in gesture
+ * mode
+ * @param mask pointer to a byte array which store the gesture mask update
+ * that want to be performed.
+ * @param size dimension in byte of mask. This size can be <=
+ * GESTURE_MASK_SIZE.
+ * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos
+ * and starting from the less significant byte.
+ * @param en 0 = enable the gestures set in mask, 1 = disable the gestures set
+ * in mask
+ * @return OK if success or an error code which specify the type of error
+ */
+int updateGestureMask(u8 *mask, int size, int en)
+{
+ u8 temp;
+ int i;
+
+ if (mask != NULL) {
+ if (size <= GESTURE_MASK_SIZE) {
+ if (en == FEAT_ENABLE) {
+ mutex_lock(&gestureMask_mutex);
+ pr_info("updateGestureMask: setting gesture mask to enable...\n");
+ if (mask != NULL)
+ for (i = 0; i < size; i++)
+ gesture_mask[i] =
+ gesture_mask[i] |
+ mask[i];
+ /* back up of the gesture enabled */
+ refreshGestureMask = 1;
+ pr_info("updateGestureMask: gesture mask to enable SET!\n");
+ mutex_unlock(&gestureMask_mutex);
+ return OK;
+ } else if (en == FEAT_DISABLE) {
+ mutex_lock(&gestureMask_mutex);
+ pr_info("updateGestureMask: setting gesture mask to disable...\n");
+ for (i = 0; i < size; i++) {
+ temp = gesture_mask[i] ^ mask[i];
+ /* enabled XOR disabled */
+ gesture_mask[i] = temp &
+ gesture_mask[i];
+ /* temp AND enabled
+ * disable the gestures that were
+ * enabled */
+ }
+ pr_info("updateGestureMask: gesture mask to disable SET!\n");
+ refreshGestureMask = 1;
+ mutex_unlock(&gestureMask_mutex);
+ return OK;
+ } else {
+ pr_err("%s: Enable parameter Invalid! %d != %d or %d ERROR %08X\n",
+ __func__,
+ en,
+ FEAT_DISABLE, FEAT_ENABLE,
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("updateGestureMask: Size not valid! %d > %d ERROR %08X\n",
+ size, GESTURE_MASK_SIZE, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("updateGestureMask: Mask NULL! ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+/**
+ * Enable in the FW the gesture mask to be used in gesture mode
+ * @param mask pointer to a byte array which store the gesture mask update
+ * that want to be sent to the FW, if NULL, will be used gesture_mask
+ * set previously without any changes.
+ * @param size dimension in byte of mask. This size can be <=
+ * GESTURE_MASK_SIZE.
+ * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos and
+ * starting from the less significant byte.
+ * @return OK if success or an error code which specify the type of error
+ */
+int enableGesture(u8 *mask, int size)
+{
+ int i, res;
+
+ pr_info("Trying to enable gesture...\n");
+
+ if (size <= GESTURE_MASK_SIZE) {
+ mutex_lock(&gestureMask_mutex);
+ if (mask != NULL)
+ for (i = 0; i < size; i++)
+ gesture_mask[i] = gesture_mask[i] | mask[i];
+ /* back up of the gesture enabled */
+
+ res = setFeatures(FEAT_SEL_GESTURE, gesture_mask,
+ GESTURE_MASK_SIZE);
+ if (res < OK) {
+ pr_err("enableGesture: ERROR %08X\n", res);
+ goto END;
+ }
+
+ pr_info("enableGesture DONE!\n");
+ res = OK;
+
+END:
+ mutex_unlock(&gestureMask_mutex);
+ return res;
+ } else {
+ pr_err("enableGesture: Size not valid! %d > %d ERROR %08X\n",
+ size, GESTURE_MASK_SIZE, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+/**
+ * Disable in the FW the gesture mask to be used in gesture mode
+ * @param mask pointer to a byte array which store the gesture mask update that
+ * want to be sent to the FW, if NULL, all the gestures will be disabled.
+ * @param size dimension in byte of mask. This size can be <=
+ * GESTURE_MASK_SIZE.
+ * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos and
+ * starting from the less significant byte.
+ * @return OK if success or an error code which specify the type of error
+ */
+int disableGesture(u8 *mask, int size)
+{
+ u8 temp;
+ int i, res;
+ u8 *pointer;
+
+
+ pr_info("Trying to disable gesture...\n");
+
+
+ if (size <= GESTURE_MASK_SIZE) {
+ mutex_lock(&gestureMask_mutex);
+ if (mask != NULL) {
+ for (i = 0; i < size; i++) {
+ temp = gesture_mask[i] ^ mask[i];
+ /* enabled mask XOR disabled mask */
+ gesture_mask[i] = temp & gesture_mask[i];
+ /* temp AND enabled
+ * disable the gestures that are specified and
+ * previously enabled */
+ }
+
+ pointer = gesture_mask;
+ } else {
+ i = 0; /* if NULL is passed disable all the possible
+ * gestures */
+ pointer = (u8 *)&i;
+ }
+
+ res = setFeatures(FEAT_SEL_GESTURE, pointer, GESTURE_MASK_SIZE);
+ if (res < OK) {
+ pr_err("disableGesture: ERROR %08X\n", res);
+ goto END;
+ }
+
+ pr_info("disableGesture DONE!\n");
+
+ res = OK;
+
+END:
+ mutex_unlock(&gestureMask_mutex);
+ return res;
+ } else {
+ pr_err("disableGesture: Size not valid! %d > %d ERROR %08X\n",
+ size, GESTURE_MASK_SIZE, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+/**
+ * Perform all the steps required to put the chip in gesture mode
+ * @param reload if set to 1, before entering in gesture mode it will re-enable
+ * in the FW the last defined gesture mask
+ * @return OK if success or an error code which specify the type of error
+ */
+int enterGestureMode(int reload)
+{
+ int res, ret;
+
+ res = fts_enableInterrupt(false);
+ if (res < OK) {
+ pr_err("enterGestureMode: ERROR %08X\n",
+ res | ERROR_DISABLE_INTER);
+ return res | ERROR_DISABLE_INTER;
+ }
+
+ if (reload == 1 || refreshGestureMask == 1) {
+ res = enableGesture(NULL, 0);
+ if (res < OK) {
+ pr_err("enterGestureMode: enableGesture ERROR %08X\n",
+ res);
+ goto END;
+ }
+
+ refreshGestureMask = 0;
+ }
+
+ res = setScanMode(SCAN_MODE_LOW_POWER, 0);
+ if (res < OK) {
+ pr_err("enterGestureMode: enter gesture mode ERROR %08X\n",
+ res);
+ goto END;
+ }
+
+ res = OK;
+END:
+ ret = fts_enableInterrupt(true);
+ if (ret < OK) {
+ pr_err("enterGestureMode: fts_enableInterrupt ERROR %08X\n",
+ res | ERROR_ENABLE_INTER);
+ res |= ret | ERROR_ENABLE_INTER;
+ }
+
+
+ return res;
+}
+
+/**
+ * Check if one or more Gesture IDs are currently enabled in gesture_mask
+ * @return FEAT_ENABLE if one or more gesture ids are enabled, FEAT_DISABLE if
+ * all the gesture ids are currently disabled
+ */
+int isAnyGestureActive(void)
+{
+ int res = 0;
+
+ while (res < (GESTURE_MASK_SIZE - 1) && gesture_mask[res] == 0)
+ /* -1 because in any case the last gesture mask byte will
+ * be evaluated with the following if */
+ res++;
+
+ if (gesture_mask[res] != 0) {
+ pr_info("%s: Active Gestures Found! gesture_mask[%d] = %02X !\n",
+ __func__, res, gesture_mask[res]);
+ return FEAT_ENABLE;
+ } else {
+ pr_info("%s: All Gestures Disabled!\n", __func__);
+ return FEAT_DISABLE;
+ }
+}
+
+
+/**
+ * Read from the frame buffer the gesture coordinates pairs of the points draw
+ * by an user when a gesture is detected
+ * @param event pointer to a byte array which contains the gesture event
+ * reported
+ * by the fw when a gesture is detected
+ * @return OK if success or an error code which specify the type of error
+ */
+int readGestureCoords(u8 *event)
+{
+ int i = 0;
+ u64 address = 0;
+ int res;
+
+ u8 val[GESTURE_MAX_COORDS_PAIRS_REPORT * 4];
+
+ /* the max coordinates to read are GESTURE_COORDS_REPORT_MAX*4
+ * (because each coordinate is a short(*2) and we have x and y) */
+
+
+ if (event[0] == EVT_ID_USER_REPORT &&
+ event[1] == EVT_TYPE_USER_GESTURE) {
+ address = (event[4] << 8) | event[3]; /* Offset in framebuff */
+ gesture_coords_reported = event[5]; /* number of pairs
+ * coords reported */
+ if (gesture_coords_reported > GESTURE_MAX_COORDS_PAIRS_REPORT) {
+ pr_err("%s: FW reported more than %d points for the gestures! Decreasing to %d\n",
+ __func__, gesture_coords_reported,
+ GESTURE_MAX_COORDS_PAIRS_REPORT);
+ gesture_coords_reported =
+ GESTURE_MAX_COORDS_PAIRS_REPORT;
+ }
+
+ pr_info("%s: Offset: %llx , coords pairs = %d\n",
+ __func__, address, gesture_coords_reported);
+
+ res = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address,
+ val, (gesture_coords_reported * 2 * 2),
+ DUMMY_FRAMEBUFFER);
+ /* *2 because each coord is made by 2 bytes,
+ * *2 because there are x and y */
+ if (res < OK) {
+ pr_err("%s: Cannot read the coordinates! ERROR %08X\n",
+ __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++) {
+ gesture_coordinates_x[i] =
+ (((u16)val[i * 2 + 1]) & 0x0F) << 8 |
+ (((u16)val[i * 2]) & 0xFF);
+ gesture_coordinates_y[i] =
+ (((u16)val[gesture_coords_reported *
+ 2 + i * 2 + 1]) & 0x0F) << 8 |
+ (((u16)val[gesture_coords_reported *
+ 2 + i * 2]) & 0xFF);
+ }
+
+
+ pr_info("%s: Reading Gesture Coordinates DONE!\n", __func__);
+ return OK;
+ } else {
+ pr_err("%s: The event passsed as argument is invalid! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+/**
+ * Return the coordinates of the points stored during the last detected gesture
+ * @param x output parameter which will store the address of the array
+ * containing the x coordinates
+ * @param y output parameter which will store the address of the array
+ * containing the y coordinates
+ * @return the number of points (x,y) stored and therefore the size of the x
+ * and y array returned.
+ */
+int getGestureCoords(u16 **x, u16 **y)
+{
+ *x = gesture_coordinates_x;
+ *y = gesture_coordinates_y;
+ pr_info("%s: Number of gesture coordinates pairs returned = %d\n",
+ __func__, gesture_coords_reported);
+ return gesture_coords_reported;
+}
diff --git a/fts_lib/ftsGesture.h b/fts_lib/ftsGesture.h
new file mode 100644
index 0000000..cb59737
--- /dev/null
+++ b/fts_lib/ftsGesture.h
@@ -0,0 +1,47 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Gesture Utilities *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsGesture.h
+ * \brief Contains all the macro and prototypes to handle the Gesture Detection
+ * features
+ */
+
+
+#ifndef FTS_GESTURE_H_
+#define FTS_GESTURE_H_
+
+
+
+
+#include "ftsHardware.h"
+
+#define GESTURE_MASK_SIZE 4 /* /< number of bytes of the
+ * gesture mask */
+
+#define GESTURE_MAX_COORDS_PAIRS_REPORT 100 /* /< max number of gestures
+ * coordinates pairs reported */
+
+
+
+int updateGestureMask(u8 *mask, int size, int en);
+int disableGesture(u8 *mask, int size);
+int enableGesture(u8 *mask, int size);
+int enterGestureMode(int reload);
+int isAnyGestureActive(void);
+int readGestureCoords(u8 *event);
+int getGestureCoords(u16 **x, u16 **y);
+
+#endif /* ! _GESTURE_H_ */
diff --git a/fts_lib/ftsHardware.h b/fts_lib/ftsHardware.h
new file mode 100644
index 0000000..20844cb
--- /dev/null
+++ b/fts_lib/ftsHardware.h
@@ -0,0 +1,319 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * HW related data *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsHardware.h
+ * \brief Contains all the definitions and information related to the IC
+ * from an hardware point of view
+ */
+
+#ifndef FTS_HARDWARE_H
+#define FTS_HARDWARE_H
+
+/* DIGITAL CHIP INFO */
+#define ALIX
+
+#ifdef ALIX
+#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */
+#define DCHIP_ID_1 0x48 /* /< MSB chip ID for FTM5 */
+#else
+#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */
+#define DCHIP_ID_1 0x39 /* /< MSB chip ID for FTM5 */
+#endif
+
+#define DCHIP_FW_VER_BYTE 2 /* /< number of bytes of the fw
+ * versions */
+
+/* CHUNKS */
+#define READ_CHUNK 1024 /* /< chunk dimension of
+ * a single i2c read,
+ * max allowed value is 2kB */
+#define WRITE_CHUNK 1024 /* /< chunk dimension of
+ * a single i2c write,
+ * max allowed value is 2kB */
+#define MEMORY_CHUNK 1024 /* /< chunk dimenasion of
+ * a single i2c write on mem,
+ * max allowed value is 2kB */
+
+#define READ_FILTERED_RAW /* /< To understand
+ * which type of RAW data to read
+ * OCTA panel needs to read Filtered RAW */
+
+/* PROTOCOL INFO */
+#define SPI_INETERFACE
+#ifdef I2C_INTERFACE
+#define I2C_SAD 0x49 /* /< slave address of the IC */
+#else
+#define SPI_DELAY_CS 10 /* /< time in usec to wait
+ * before rising the CS */
+#endif
+
+#define IER_ENABLE 0x41 /* /< value to write in IER_ADDR
+ * to enable the interrupts */
+#define IER_DISABLE 0x00 /* /< value to write in IER_ADDR
+ * to disable the interrupts
+ **/
+
+/* FLASH COMMAND */
+/** @defgroup flash_command Flash Commands
+ * All the commands that works with the flash of the IC
+ * @{
+ */
+#define FLASH_CMD_UNLOCK 0xF7
+
+#define FLASH_CMD_READ_REGISTER 0xFA
+#define FLASH_CMD_WRITE_REGISTER 0xFA
+
+/* FLASH UNLOCK PARAMETER */
+#define FLASH_UNLOCK_CODE0 0x25
+#define FLASH_UNLOCK_CODE1 0x20
+#define FLASH_UNLOCK_CODE2 0x6B
+#define FLASH_UNLOCK_CODE3 0x00
+
+/* FLASH UVLO ENABLE PARAMETER */
+#define FLASH_UVLO_ENABLE_CODE0 0x1B
+#define FLASH_UVLO_ENABLE_CODE1 0x66
+
+/* FLASH AUTOPOWERDOWN ENABLE PARAMETER */
+#define FLASH_AUTOPOWERDOWN_ENABLE_CODE0 0x68
+#define FLASH_AUTOPOWERDOWN_ENABLE_CODE1 0x13
+
+#define INFO_BLOCK_SIZE 16384
+#define INFO_BLOCK_LOCKDOWN 0x1800
+#define INFO_BLOCK_IOFF 0x19E8
+#define INFO_BLOCK_OSC 0x1EAC
+#define INFO_BLOCK_AOFFSET 0x1D48
+#define INFO_BLOCK_CG 0x1E08
+
+/* FLASH ERASE and DMA PARAMETER */
+#define FLASH_ERASE_START 0x80
+#define FLASH_ERASE_CODE1 0xC0
+#define FLASH_DMA_CODE1 0xC0
+#define FLASH_ERASE_UNLOCK_CODE0 0xDE
+#define FLASH_ERASE_UNLOCK_CODE1 0x03
+#define FLASH_ERASE_CODE0 0x6A
+#define FLASH_DMA_CODE0 0x71
+#define FLASH_DMA_CONFIG 0x72
+#define FLASH_NUM_PAGE 32 /* /< number of pages in main
+ * flash */
+#define FLASH_CX_PAGE_START 28 /* /< starting page which
+ * contain Cx data */
+#define FLASH_CX_PAGE_END 30 /* /< last page which contain Cx
+ * data */
+#define FLASH_PANEL_PAGE_START 26 /* /< starting page which
+ * contain Panel Init data */
+#define FLASH_PANEL_PAGE_END 27 /* /< last page which contain
+ * Panel Init data */
+/** @} */
+
+/* FLASH ADDRESS */
+#define FLASH_ADDR_CODE 0x00000000 /* /< address of code in
+ * FTI */
+#define FLASH_ADDR_CONFIG 0x00007C00 /* /< address of config
+ * in FTI */
+#define FLASH_ADDR_CX 0x00007000 /* /< address of Init
+ * data in FTI */
+
+/* SIZES FW, CODE, CONFIG, MEMH */
+/** @defgroup fw_file FW file info
+ * All the info related to the fw file
+ * @{
+ */
+#define FW_HEADER_SIZE 64 /* /< dimension of the header in
+ * .fts file */
+#define FW_HEADER_SIGNATURE 0xAA55AA55 /* /< header signature
+ * */
+#define FW_FTB_VER 0x00000001 /* /< .ftb version */
+#define FW_BYTES_ALIGN 4 /* /< allignment of the info */
+#define FW_BIN_VER_OFFSET 16 /* /< offset of the fw version
+ * in the .ftb file */
+#define FW_BIN_CONFIG_ID_OFFSET 20 /* /< offset of the config id in
+ * the .ftb file */
+#define FW_CX_VERSION (16 + 4)/* CX version offset in sec2 of
+ * FW */
+/** @} */
+
+/* FIFO */
+#define FIFO_EVENT_SIZE 8 /* /< number of bytes of one
+ * event */
+#define FIFO_DEPTH 32 /* /< max number of events that
+ * the FIFO can collect before
+ * going in overflow in FTM5 */
+
+#ifdef I2C_INTERFACE
+#define FIFO_CMD_READALL 0x86 /* /< command to read all
+ * the events in the FIFO */
+#else
+#define FIFO_CMD_READALL 0x87 /* /< command to read all
+ * the events in the FIFO */
+#endif
+#define FIFO_CMD_READONE FIFO_CMD_READALL/* /< commad to read
+ * one event from FIFO
+ **/
+
+
+/* OP CODES FOR MEMORY (based on protocol) */
+#ifdef I2C_INTERFACE
+#define FTS_CMD_HW_REG_R 0xFA /* /< command to read an hw register if
+ * FTI */
+#define FTS_CMD_HW_REG_W 0xFA /* /< command to write an hw register if
+ * FTI */
+#define FTS_CMD_FRAMEBUFFER_W 0xA6 /* /< command to write the framebuffer
+ * if FTI
+ */
+#define FTS_CMD_FRAMEBUFFER_R 0xA6 /* /< command to read the framebuffer if
+ * FTI */
+#define FTS_CMD_CONFIG_R 0xA8 /* /< command to read the config memory
+ * if FTI */
+#define FTS_CMD_CONFIG_W 0xA8 /* /< command to write the config memory
+ * if FTI */
+#else
+#define FTS_CMD_HW_REG_R 0xFB /* /< command to read an hw register if
+ * FTI */
+#define FTS_CMD_HW_REG_W 0xFA /* /< command to write an hw register if
+ * FTI */
+#define FTS_CMD_FRAMEBUFFER_W 0xA6 /* /< command to write the framebuffer
+ * if FTI
+ */
+#define FTS_CMD_FRAMEBUFFER_R 0xA7 /* /< command to read the framebuffer if
+ * FTI */
+#define FTS_CMD_CONFIG_R 0xA9 /* /< command to read the config memory
+ * if FTI */
+#define FTS_CMD_CONFIG_W 0xA8 /* /< command to write the config memory
+ * if FTI */
+#endif
+
+/* DUMMY BYTES DATA */
+#ifndef I2C_INTERFACE
+#define DUMMY_HW_REG 1 /* /< 1 if the first byte read from HW
+ * register is dummy */
+#define DUMMY_FRAMEBUFFER 1 /* /< 1 if the first byte read from
+ * Frame buffer is dummy */
+#define DUMMY_CONFIG 1 /* /< 1 if the first byte read from
+ * Config Memory is dummy */
+#define DUMMY_FIFO 1 /* /< 1 if the first byte read from FIFO
+ * is dummy */
+#else
+#define DUMMY_HW_REG 0 /* /< 1 if the first byte read from HW
+ * register is dummy */
+#define DUMMY_FRAMEBUFFER 0 /* /< 1 if the first byte read from
+ * Frame buffer is dummy */
+#define DUMMY_CONFIG 0 /* /< 1 if the first byte read from
+ * Config Memory is dummy */
+#define DUMMY_FIFO 0 /* /< 1 if the first byte read from FIFO
+ * is dummy */
+#endif
+
+/** @defgroup hw_adr HW Address
+ * @ingroup address
+ * Important addresses of hardware registers (and sometimes their important
+ * values)
+ * @{
+ */
+
+/* IMPORTANT HW ADDRESSES (u64) */
+#define ADDR_FRAMEBUFFER ((u64)0x0000000000000000) /* /< frame
+ * buffer
+ * address in
+ * memory */
+#define ADDR_ERROR_DUMP ((u64)0x000000000000EF80) /* /< start
+ * address dump
+ * error log */
+
+/* SYSTEM RESET INFO */
+#define ADDR_SYSTEM_RESET ((u64)0x0000000020000024) /* /< address of
+ * System
+ * control
+ * register in
+ * FTI */
+/* value to write in SYSTEM_RESET_ADDRESS to perform a system reset in FTM5 */
+#define SYSTEM_RESET_VALUE 0x81
+
+
+/* REMAP REGISTER */
+#define ADDR_BOOT_OPTION ((u64)0x0000000020000025) /* /< address of
+ * Boot option
+ * register */
+
+/* INTERRUPT INFO */
+#define ADDR_IER ((u64)0x0000000020000029) /* /< address of
+ * the Interrupt
+ * enable
+ * register in
+ * FTMI */
+
+/* Chip ID/Fw Version */
+#define ADDR_DCHIP_ID ((u64)0x0000000020000000) /* /< chip id
+ * address for
+ * FTI */
+#define ADDR_DCHIP_FW_VER ((u64)0x0000000020000004) /* /< fw version
+ * address for
+ * FTI */
+
+/* INTERFACE REGISTER */
+#define ADDR_ICR ((u64)0x000000002000002D) /* /< address of
+ * Device
+ * control
+ * register
+ * to set the
+ * communication
+ * protocol
+ * (SPI/I2C) */
+
+#define SPI4_MASK 0x02 /* /< bit to set spi4 */
+
+#define ADDR_FLASH_STATUS ((u64)0x0000000020000068)
+#define ADDR_INFOBLOCK ((u64)0x0000000000040000)
+
+
+/* CRC ADDR */
+#define ADDR_CRC ((u64)0x0000000020000078) /* /< address of
+ * the CRC
+ * control
+ * register in
+ * FTI */
+#define CRC_MASK 0x03 /* /< bitmask which reveal if
+ * there is a CRC error in the flash */
+
+#define ADDR_CONFIG_OFFSET ((u64)0x0000000000000000) /* /< config
+ * address in
+ * memory if FTI
+ * */
+
+#define ADDR_GPIO_INPUT ((u64)0x0000000020000030) /* /< address of
+ * GPIO input
+ * register */
+#define ADDR_GPIO_DIRECTION ((u64)0x0000000020000032) /* /< address of
+ * GPIO
+ * direction
+ * register */
+#define ADDR_GPIO_PULLUP ((u64)0x0000000020000034) /* /< address of
+ * GPIO pullup
+ * register */
+#define ADDR_GPIO_CONFIG_REG0 ((u64)0x000000002000003D) /* /< address of
+ * GPIO config
+ * register */
+#define ADDR_GPIO_CONFIG_REG2 ((u64)0x000000002000003F) /* /< address of
+ * GPIO config
+ * register */
+
+#define ADDR_GPIO_CONFIG_REG3 ((u64)0x000000002000003E) /* /< address of
+ * GPIO config
+ * register */
+
+/**@}*/
+
+
+#endif
diff --git a/fts_lib/ftsIO.c b/fts_lib/ftsIO.c
new file mode 100644
index 0000000..9443fdd
--- /dev/null
+++ b/fts_lib/ftsIO.c
@@ -0,0 +1,924 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * I2C/SPI Communication *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsIO.c
+ * \brief Contains all the functions which handle with the I2C/SPI
+ *communication
+ */
+
+
+#include "ftsSoftware.h"
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/of_gpio.h>
+
+#ifdef I2C_INTERFACE
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+static u16 I2CSAD; /* /< slave address of the IC in the i2c bus */
+#else
+#include <linux/spi/spidev.h>
+#endif
+
+static void *client; /* /< bus client retrived by the OS and
+ * used to execute the bus transfers */
+
+
+
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsHardware.h"
+#include "ftsIO.h"
+
+
+/**
+ * Initialize the static client variable of the fts_lib library in order
+ * to allow any i2c/spi transaction in the driver (Must be called in the probe)
+ * @param clt pointer to i2c_client or spi_device struct which identify the bus
+ * slave device
+ * @return OK
+ */
+int openChannel(void *clt)
+{
+ client = clt;
+#ifdef I2C_INTERFACE
+ I2CSAD = ((struct i2c_client *)clt)->addr;
+ pr_info("openChannel: SAD: %02X\n", I2CSAD);
+#else
+ pr_info("%s: spi_master: flags = %04X !\n", __func__,
+ ((struct spi_device *)client)->master->flags);
+ pr_info("%s: spi_device: max_speed = %d chip select = %02X bits_per_words = %d mode = %04X !\n",
+ __func__, ((struct spi_device *)client)->max_speed_hz,
+ ((struct spi_device *)client)->chip_select,
+ ((struct spi_device *)client)->bits_per_word,
+ ((struct spi_device *)client)->mode);
+ pr_info("openChannel: completed!\n");
+#endif
+ return OK;
+}
+
+#ifdef I2C_INTERFACE
+/**
+ * Change the I2C slave address which will be used during the transaction
+ * (For Debug Only)
+ * @param sad new slave address id
+ * @return OK
+ */
+int changeSAD(u8 sad)
+{
+ I2CSAD = sad;
+ return OK;
+}
+#endif
+
+
+/**
+ * Retrieve the pointer to the device struct of the IC
+ * @return a the device struct pointer if client was previously set
+ * or NULL in all the other cases
+ */
+struct device *getDev(void)
+{
+ if (client != NULL)
+ return &(getClient()->dev);
+ else
+ return NULL;
+}
+
+
+#ifdef I2C_INTERFACE
+/**
+ * Retrieve the pointer of the i2c_client struct representing the IC as i2c
+ * slave
+ * @return client if it was previously set or NULL in all the other cases
+ */
+struct i2c_client *getClient()
+{
+ if (client != NULL)
+ return (struct i2c_client *)client;
+ else
+ return NULL;
+}
+#else
+/**
+ * Retrieve the pointer of the spi_device struct representing the IC as spi
+ * slave
+ * @return client if it was previously set or NULL in all the other cases
+ */
+struct spi_device *getClient()
+{
+ if (client != NULL)
+ return (struct spi_device *)client;
+ else
+ return NULL;
+}
+#endif
+
+struct fts_ts_info *getDrvInfo(void)
+{
+ struct device *dev = getDev();
+ struct fts_ts_info *info = NULL;
+
+ if (dev != NULL)
+ info = dev_get_drvdata(dev);
+ return info;
+}
+
+/****************** New I2C API *********************/
+
+/**
+ * Perform a direct bus read
+ * @param outBuf pointer of a byte array which should contain the byte read
+ * from the IC
+ * @param byteToRead number of bytes to read
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_read_internal(u8 *outBuf, int byteToRead, bool dma_safe)
+{
+ int ret = -1;
+ int retry = 0;
+ struct fts_ts_info *info = getDrvInfo();
+#ifdef I2C_INTERFACE
+ struct i2c_msg I2CMsg[1];
+#else
+ struct spi_message msg;
+ struct spi_transfer transfer[1] = { { 0 } };
+#endif
+
+ if (dma_safe == false && byteToRead > sizeof(info->io_read_buf)) {
+ pr_err("%s: preallocated buffers are too small!\n", __func__);
+ return ERROR_ALLOC;
+ }
+
+#ifdef I2C_INTERFACE
+ I2CMsg[0].addr = (__u16)I2CSAD;
+ I2CMsg[0].flags = (__u16)I2C_M_RD;
+ I2CMsg[0].len = (__u16)byteToRead;
+ if (dma_safe == false)
+ I2CMsg[0].buf = (__u8 *)info->io_read_buf;
+ else
+ I2CMsg[0].buf = (__u8 *)outBuf;
+#else
+ spi_message_init(&msg);
+
+ transfer[0].len = byteToRead;
+ transfer[0].delay_usecs = SPI_DELAY_CS;
+ transfer[0].tx_buf = NULL;
+ if (dma_safe == false)
+ transfer[0].rx_buf = info->io_read_buf;
+ else
+ transfer[0].rx_buf = outBuf;
+ spi_message_add_tail(&transfer[0], &msg);
+#endif
+
+ if (client == NULL)
+ return ERROR_BUS_O;
+ while (retry < I2C_RETRY && ret < OK) {
+#ifdef I2C_INTERFACE
+ ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
+#else
+ ret = spi_sync(getClient(), &msg);
+#endif
+
+ retry++;
+ if (ret < OK)
+ mdelay(I2C_WAIT_BEFORE_RETRY);
+ /* pr_err("fts_writeCmd: attempt %d\n", retry); */
+ }
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_R);
+ return ERROR_BUS_R;
+ }
+
+ if (dma_safe == false)
+ memcpy(outBuf, info->io_read_buf, byteToRead);
+
+ return OK;
+}
+
+
+/**
+ * Perform a bus write followed by a bus read without a stop condition
+ * @param cmd byte array containing the command to write
+ * @param cmdLength size of cmd
+ * @param outBuf pointer of a byte array which should contain the bytes read
+ * from the IC
+ * @param byteToRead number of bytes to read
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_writeRead_internal(u8 *cmd, int cmdLength, u8 *outBuf,
+ int byteToRead, bool dma_safe)
+{
+ int ret = -1;
+ int retry = 0;
+ struct fts_ts_info *info = getDrvInfo();
+#ifdef I2C_INTERFACE
+ struct i2c_msg I2CMsg[2];
+#else
+ struct spi_message msg;
+ struct spi_transfer transfer[2] = { { 0 }, { 0 } };
+#endif
+
+ if (dma_safe == false && (cmdLength > sizeof(info->io_write_buf) ||
+ byteToRead > sizeof(info->io_read_buf))) {
+ pr_err("%s: preallocated buffers are too small!\n", __func__);
+ return ERROR_ALLOC;
+ }
+
+#ifdef I2C_INTERFACE
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ /* write msg */
+ I2CMsg[0].addr = (__u16)I2CSAD;
+ I2CMsg[0].flags = (__u16)0;
+ I2CMsg[0].len = (__u16)cmdLength;
+ I2CMsg[0].buf = (__u8 *)cmd;
+
+ /* read msg */
+ I2CMsg[1].addr = (__u16)I2CSAD;
+ I2CMsg[1].flags = I2C_M_RD;
+ I2CMsg[1].len = byteToRead;
+ if (dma_safe == false)
+ I2CMsg[1].buf = (__u8 *)info->io_read_buf;
+ else
+ I2CMsg[1].buf = (__u8 *)outBuf;
+#else
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ spi_message_init(&msg);
+
+ transfer[0].len = cmdLength;
+ transfer[0].tx_buf = cmd;
+ transfer[0].rx_buf = NULL;
+ spi_message_add_tail(&transfer[0], &msg);
+
+ transfer[1].len = byteToRead;
+ transfer[1].delay_usecs = SPI_DELAY_CS;
+ transfer[1].tx_buf = NULL;
+ if (dma_safe == false)
+ transfer[1].rx_buf = info->io_read_buf;
+ else
+ transfer[1].rx_buf = outBuf;
+ spi_message_add_tail(&transfer[1], &msg);
+
+#endif
+
+ if (client == NULL)
+ return ERROR_BUS_O;
+
+ while (retry < I2C_RETRY && ret < OK) {
+#ifdef I2C_INTERFACE
+ ret = i2c_transfer(getClient()->adapter, I2CMsg, 2);
+#else
+ ret = spi_sync(getClient(), &msg);
+#endif
+
+ retry++;
+ if (ret < OK)
+ mdelay(I2C_WAIT_BEFORE_RETRY);
+ }
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+
+ if (dma_safe == false)
+ memcpy(outBuf, info->io_read_buf, byteToRead);
+
+ return OK;
+}
+
+
+/**
+ * Perform a bus write
+ * @param cmd byte array containing the command to write
+ * @param cmdLength size of cmd
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_write_internal(u8 *cmd, int cmdLength, bool dma_safe)
+{
+ int ret = -1;
+ int retry = 0;
+ struct fts_ts_info *info = getDrvInfo();
+#ifdef I2C_INTERFACE
+ struct i2c_msg I2CMsg[1];
+#else
+ struct spi_message msg;
+ struct spi_transfer transfer[1] = { { 0 } };
+#endif
+
+ if (dma_safe == false && cmdLength > sizeof(info->io_write_buf)) {
+ pr_err("%s: preallocated buffers are too small!\n", __func__);
+ return ERROR_ALLOC;
+ }
+
+#ifdef I2C_INTERFACE
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ I2CMsg[0].addr = (__u16)I2CSAD;
+ I2CMsg[0].flags = (__u16)0;
+ I2CMsg[0].len = (__u16)cmdLength;
+ I2CMsg[0].buf = (__u8 *)cmd;
+#else
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ spi_message_init(&msg);
+
+ transfer[0].len = cmdLength;
+ transfer[0].delay_usecs = SPI_DELAY_CS;
+ transfer[0].tx_buf = cmd;
+ transfer[0].rx_buf = NULL;
+ spi_message_add_tail(&transfer[0], &msg);
+#endif
+
+
+ if (client == NULL)
+ return ERROR_BUS_O;
+ while (retry < I2C_RETRY && ret < OK) {
+#ifdef I2C_INTERFACE
+ ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
+#else
+ ret = spi_sync(getClient(), &msg);
+#endif
+
+ retry++;
+ if (ret < OK)
+ mdelay(I2C_WAIT_BEFORE_RETRY);
+ /* pr_err("fts_writeCmd: attempt %d\n", retry); */
+ }
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+ return OK;
+}
+
+/**
+ * Write a FW command to the IC and check automatically the echo event
+ * @param cmd byte array containing the command to send
+ * @param cmdLength size of cmd
+ * @return OK if success, or an error code which specify the type of error
+ */
+static int fts_writeFwCmd_internal(u8 *cmd, int cmdLength, bool dma_safe)
+{
+ int ret = -1;
+ int ret2 = -1;
+ int retry = 0;
+ struct fts_ts_info *info = getDrvInfo();
+#ifdef I2C_INTERFACE
+ struct i2c_msg I2CMsg[1];
+#else
+ struct spi_message msg;
+ struct spi_transfer transfer[1] = { { 0 } };
+#endif
+
+ if (dma_safe == false && cmdLength > sizeof(info->io_write_buf)) {
+ pr_err("%s: preallocated buffers are too small!\n", __func__);
+ return ERROR_ALLOC;
+ }
+
+#ifdef I2C_INTERFACE
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ I2CMsg[0].addr = (__u16)I2CSAD;
+ I2CMsg[0].flags = (__u16)0;
+ I2CMsg[0].len = (__u16)cmdLength;
+ I2CMsg[0].buf = (__u8 *)cmd;
+#else
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, cmd, cmdLength);
+ cmd = info->io_write_buf;
+ }
+
+ spi_message_init(&msg);
+
+ transfer[0].len = cmdLength;
+ transfer[0].delay_usecs = SPI_DELAY_CS;
+ transfer[0].tx_buf = cmd;
+ transfer[0].rx_buf = NULL;
+ spi_message_add_tail(&transfer[0], &msg);
+#endif
+
+ if (client == NULL)
+ return ERROR_BUS_O;
+ resetErrorList();
+ while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) {
+#ifdef I2C_INTERFACE
+ ret = i2c_transfer(getClient()->adapter, I2CMsg, 1);
+#else
+ ret = spi_sync(getClient(), &msg);
+#endif
+ retry++;
+ if (ret >= 0)
+ ret2 = checkEcho(cmd, cmdLength);
+ if (ret < OK || ret2 < OK)
+ mdelay(I2C_WAIT_BEFORE_RETRY);
+ /* pr_err("fts_writeCmd: attempt %d\n", retry); */
+ }
+ if (ret < 0) {
+ pr_err("fts_writeFwCmd: ERROR %08X\n", ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+ if (ret2 < OK) {
+ pr_err("fts_writeFwCmd: check echo ERROR %08X\n", ret2);
+ return ret2;
+ }
+ return OK;
+}
+
+
+/**
+ * Perform two bus write and one bus read without any stop condition
+ * In case of FTI this function is not supported and the same sequence
+ * can be achieved calling fts_write followed by an fts_writeRead.
+ * @param writeCmd1 byte array containing the first command to write
+ * @param writeCmdLength size of writeCmd1
+ * @param readCmd1 byte array containing the second command to write
+ * @param readCmdLength size of readCmd1
+ * @param outBuf pointer of a byte array which should contain the bytes read
+ * from the IC
+ * @param byteToRead number of bytes to read
+ * @return OK if success or an error code which specify the type of error
+ */
+static int fts_writeThenWriteRead_internal(u8 *writeCmd1, int writeCmdLength,
+ u8 *readCmd1, int readCmdLength,
+ u8 *outBuf, int byteToRead,
+ bool dma_safe)
+{
+ int ret = -1;
+ int retry = 0;
+ struct fts_ts_info *info = getDrvInfo();
+#ifdef I2C_INTERFACE
+ struct i2c_msg I2CMsg[3];
+#else
+ struct spi_message msg;
+ struct spi_transfer transfer[3] = { { 0 }, { 0 }, { 0 } };
+#endif
+
+ if (dma_safe == false && (writeCmdLength > sizeof(info->io_write_buf) ||
+ readCmdLength > sizeof(info->io_extra_write_buf) ||
+ byteToRead > sizeof(info->io_read_buf))) {
+ pr_err("%s: preallocated buffers are too small!\n", __func__);
+ return ERROR_ALLOC;
+ }
+
+#ifdef I2C_INTERFACE
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, writeCmd1, writeCmdLength);
+ writeCmd1 = info->io_write_buf;
+ memcpy(info->io_extra_write_buf, readCmd1, readCmdLength);
+ readCmd1 = info->io_extra_write_buf;
+ }
+
+ /* write msg */
+ I2CMsg[0].addr = (__u16)I2CSAD;
+ I2CMsg[0].flags = (__u16)0;
+ I2CMsg[0].len = (__u16)writeCmdLength;
+ I2CMsg[0].buf = (__u8 *)writeCmd1;
+
+ /* write msg */
+ I2CMsg[1].addr = (__u16)I2CSAD;
+ I2CMsg[1].flags = (__u16)0;
+ I2CMsg[1].len = (__u16)readCmdLength;
+ I2CMsg[1].buf = (__u8 *)readCmd1;
+
+ /* read msg */
+ I2CMsg[2].addr = (__u16)I2CSAD;
+ I2CMsg[2].flags = I2C_M_RD;
+ I2CMsg[2].len = byteToRead;
+ if (dma_safe == false)
+ I2CMsg[2].buf = (__u8 *)info->io_read_buf;
+ else
+ I2CMsg[2].buf = (__u8 *)outBuf;
+#else
+ if (dma_safe == false) {
+ memcpy(info->io_write_buf, writeCmd1, writeCmdLength);
+ writeCmd1 = info->io_write_buf;
+ memcpy(info->io_extra_write_buf, readCmd1, readCmdLength);
+ readCmd1 = info->io_extra_write_buf;
+ }
+
+ spi_message_init(&msg);
+
+ transfer[0].len = writeCmdLength;
+ transfer[0].tx_buf = writeCmd1;
+ transfer[0].rx_buf = NULL;
+ spi_message_add_tail(&transfer[0], &msg);
+
+ transfer[1].len = readCmdLength;
+ transfer[1].tx_buf = readCmd1;
+ transfer[1].rx_buf = NULL;
+ spi_message_add_tail(&transfer[1], &msg);
+
+ transfer[2].len = byteToRead;
+ transfer[2].delay_usecs = SPI_DELAY_CS;
+ transfer[2].tx_buf = NULL;
+ if (dma_safe == false)
+ transfer[2].rx_buf = info->io_read_buf;
+ else
+ transfer[2].rx_buf = outBuf;
+ spi_message_add_tail(&transfer[2], &msg);
+#endif
+
+ if (client == NULL)
+ return ERROR_BUS_O;
+ while (retry < I2C_RETRY && ret < OK) {
+#ifdef I2C_INTERFACE
+ ret = i2c_transfer(getClient()->adapter, I2CMsg, 3);
+#else
+ ret = spi_sync(getClient(), &msg);
+#endif
+ retry++;
+ if (ret < OK)
+ mdelay(I2C_WAIT_BEFORE_RETRY);
+ }
+
+ if (ret < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+
+ if (dma_safe == false)
+ memcpy(outBuf, info->io_read_buf, byteToRead);
+
+ return OK;
+}
+
+/* Wrapper API for i2c read and write */
+int fts_read(u8 *outBuf, int byteToRead)
+{
+ return fts_read_internal(outBuf, byteToRead, false);
+}
+
+int fts_read_heap(u8 *outBuf, int byteToRead)
+{
+ return fts_read_internal(outBuf, byteToRead, true);
+}
+
+int fts_writeRead(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead)
+{
+ return fts_writeRead_internal(cmd, cmdLength, outBuf, byteToRead,
+ false);
+}
+
+int fts_writeRead_heap(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead)
+{
+ return fts_writeRead_internal(cmd, cmdLength, outBuf, byteToRead, true);
+}
+
+int fts_write(u8 *cmd, int cmdLength)
+{
+ return fts_write_internal(cmd, cmdLength, false);
+}
+
+int fts_write_heap(u8 *cmd, int cmdLength)
+{
+ return fts_write_internal(cmd, cmdLength, true);
+}
+
+int fts_writeFwCmd(u8 *cmd, int cmdLength)
+{
+ return fts_writeFwCmd_internal(cmd, cmdLength, false);
+}
+
+int fts_writeFwCmd_heap(u8 *cmd, int cmdLength)
+{
+ return fts_writeFwCmd_internal(cmd, cmdLength, true);
+}
+
+int fts_writeThenWriteRead(u8 *writeCmd1, int writeCmdLength,
+ u8 *readCmd1, int readCmdLength,
+ u8 *outBuf, int byteToRead)
+{
+ return fts_writeThenWriteRead_internal(writeCmd1, writeCmdLength,
+ readCmd1, readCmdLength,
+ outBuf, byteToRead, false);
+}
+
+int fts_writeThenWriteRead_heap(u8 *writeCmd1, int writeCmdLength,
+ u8 *readCmd1, int readCmdLength,
+ u8 *outBuf, int byteToRead)
+{
+ return fts_writeThenWriteRead_internal(writeCmd1, writeCmdLength,
+ readCmd1, readCmdLength,
+ outBuf, byteToRead, true);
+}
+
+/**
+ * Perform a chunked write with one byte op code and 1 to 8 bytes address
+ * @param cmd byte containing the op code to write
+ * @param addrSize address size in byte
+ * @param address the starting address
+ * @param data pointer of a byte array which contain the bytes to write
+ * @param dataSize size of data
+ * @return OK if success or an error code which specify the type of error
+ */
+/* this function works only if the address is max 8 bytes */
+int fts_writeU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *data,
+ int dataSize)
+{
+ u8 *finalCmd;
+ int remaining = dataSize;
+ int toWrite = 0, i = 0;
+ struct fts_ts_info *info = getDrvInfo();
+
+ finalCmd = info->io_write_buf;
+
+ if (addrSize <= sizeof(u64)) {
+ while (remaining > 0) {
+ if (remaining >= WRITE_CHUNK) {
+ toWrite = WRITE_CHUNK;
+ remaining -= WRITE_CHUNK;
+ } else {
+ toWrite = remaining;
+ remaining = 0;
+ }
+
+ finalCmd[0] = cmd;
+ pr_debug("%s: addrSize = %d\n", __func__, addrSize);
+ for (i = 0; i < addrSize; i++) {
+ finalCmd[i + 1] = (u8)((address >> ((addrSize -
+ 1 - i) *
+ 8)) & 0xFF);
+ pr_debug("%s: cmd[%d] = %02X\n",
+ __func__, i + 1, finalCmd[i + 1]);
+ }
+
+ memcpy(&finalCmd[addrSize + 1], data, toWrite);
+
+ if (fts_write_heap(finalCmd, 1 + addrSize + toWrite)
+ < OK) {
+ pr_err(" %s: ERROR %08X\n",
+ __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ address += toWrite;
+
+ data += toWrite;
+ }
+ } else
+ pr_err("%s: address size bigger than max allowed %lu... ERROR %08X\n",
+ __func__, sizeof(u64), ERROR_OP_NOT_ALLOW);
+
+ return OK;
+}
+
+/**
+ * Perform a chunked write read with one byte op code and 1 to 8 bytes address
+ * and dummy byte support.
+ * @param cmd byte containing the op code to write
+ * @param addrSize address size in byte
+ * @param address the starting address
+ * @param outBuf pointer of a byte array which contain the bytes to read
+ * @param byteToRead number of bytes to read
+ * @param hasDummyByte if the first byte of each reading is dummy (must be
+ * skipped)
+ * set to 1, otherwise if it is valid set to 0 (or any other value)
+ * @return OK if success or an error code which specify the type of error
+ */
+int fts_writeReadU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *outBuf,
+ int byteToRead, int hasDummyByte)
+{
+ u8 *finalCmd;
+ u8 *buff;
+ int remaining = byteToRead;
+ int toRead = 0, i = 0;
+ struct fts_ts_info *info = getDrvInfo();
+
+ finalCmd = info->io_write_buf;
+ buff = info->io_read_buf;
+
+ while (remaining > 0) {
+ if (remaining >= READ_CHUNK) {
+ toRead = READ_CHUNK;
+ remaining -= READ_CHUNK;
+ } else {
+ toRead = remaining;
+ remaining = 0;
+ }
+
+ finalCmd[0] = cmd;
+ for (i = 0; i < addrSize; i++)
+ finalCmd[i + 1] = (u8)((address >> ((addrSize - 1 - i) *
+ 8)) & 0xFF);
+
+ if (hasDummyByte == 1) {
+ if (fts_writeRead_heap(finalCmd, 1 + addrSize, buff,
+ toRead + 1) < OK) {
+ pr_err("%s: read error... ERROR %08X\n",
+ __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+ memcpy(outBuf, buff + 1, toRead);
+ } else {
+ if (fts_writeRead_heap(finalCmd, 1 + addrSize, buff,
+ toRead) < OK) {
+ pr_err("%s: read error... ERROR %08X\n",
+ __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+ memcpy(outBuf, buff, toRead);
+ }
+
+ address += toRead;
+
+ outBuf += toRead;
+ }
+
+ return OK;
+}
+
+/**
+ * Perform a chunked write followed by a second write with one byte op code
+ * for each write and 1 to 8 bytes address (the sum of the 2 address size of
+ * the two writes can not exceed 8 bytes)
+ * @param cmd1 byte containing the op code of first write
+ * @param addrSize1 address size in byte of first write
+ * @param cmd2 byte containing the op code of second write
+ * @param addrSize2 address size in byte of second write
+ * @param address the starting address
+ * @param data pointer of a byte array which contain the bytes to write
+ * @param dataSize size of data
+ * @return OK if success or an error code which specify the type of error
+ */
+/* this function works only if the sum of two addresses in the two commands is
+ * max 8 bytes */
+int fts_writeU8UXthenWriteU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2,
+ AddrSize addrSize2, u64 address, u8 *data,
+ int dataSize)
+{
+ u8 *finalCmd1;
+ u8 *finalCmd2;
+ int remaining = dataSize;
+ int toWrite = 0, i = 0;
+ struct fts_ts_info *info = getDrvInfo();
+
+ finalCmd1 = info->io_write_buf;
+ finalCmd2 = info->io_extra_write_buf;
+
+ while (remaining > 0) {
+ if (remaining >= WRITE_CHUNK) {
+ toWrite = WRITE_CHUNK;
+ remaining -= WRITE_CHUNK;
+ } else {
+ toWrite = remaining;
+ remaining = 0;
+ }
+
+ finalCmd1[0] = cmd1;
+ for (i = 0; i < addrSize1; i++)
+ finalCmd1[i + 1] = (u8)((address >> ((addrSize1 +
+ addrSize2 - 1 -
+ i) * 8)) & 0xFF);
+
+ finalCmd2[0] = cmd2;
+ for (i = addrSize1; i < addrSize1 + addrSize2; i++)
+ finalCmd2[i - addrSize1 + 1] = (u8)((address >>
+ ((addrSize1 +
+ addrSize2 - 1 -
+ i) * 8)) & 0xFF);
+
+ memcpy(&finalCmd2[addrSize2 + 1], data, toWrite);
+
+ if (fts_write_heap(finalCmd1, 1 + addrSize1) < OK) {
+ pr_err("%s: first write error... ERROR %08X\n",
+ __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ if (fts_write_heap(finalCmd2, 1 + addrSize2 + toWrite) < OK) {
+ pr_err("%s: second write error... ERROR %08X\n",
+ __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ address += toWrite;
+
+ data += toWrite;
+ }
+
+ return OK;
+}
+
+/**
+ * Perform a chunked write followed by a write read with one byte op code
+ * and 1 to 8 bytes address for each write and dummy byte support.
+ * @param cmd1 byte containing the op code of first write
+ * @param addrSize1 address size in byte of first write
+ * @param cmd2 byte containing the op code of second write read
+ * @param addrSize2 address size in byte of second write read
+ * @param address the starting address
+ * @param outBuf pointer of a byte array which contain the bytes to read
+ * @param byteToRead number of bytes to read
+ * @param hasDummyByte if the first byte of each reading is dummy (must be
+ * skipped) set to 1,
+ * otherwise if it is valid set to 0 (or any other value)
+ * @return OK if success or an error code which specify the type of error
+ */
+/* this function works only if the sum of two addresses in the two commands is
+ * max 8 bytes */
+int fts_writeU8UXthenWriteReadU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2,
+ AddrSize addrSize2, u64 address, u8 *outBuf,
+ int byteToRead, int hasDummyByte)
+{
+ u8 *finalCmd1;
+ u8 *finalCmd2;
+ u8 *buff;
+ int remaining = byteToRead;
+ int toRead = 0, i = 0;
+ struct fts_ts_info *info = getDrvInfo();
+
+ finalCmd1 = info->io_write_buf;
+ finalCmd2 = info->io_extra_write_buf;
+ buff = info->io_read_buf;
+
+ while (remaining > 0) {
+ if (remaining >= READ_CHUNK) {
+ toRead = READ_CHUNK;
+ remaining -= READ_CHUNK;
+ } else {
+ toRead = remaining;
+ remaining = 0;
+ }
+
+
+ finalCmd1[0] = cmd1;
+ for (i = 0; i < addrSize1; i++)
+ finalCmd1[i + 1] = (u8)((address >> ((addrSize1 +
+ addrSize2 - 1 -
+ i) * 8)) & 0xFF);
+
+ finalCmd2[0] = cmd2;
+ for (i = addrSize1; i < addrSize1 + addrSize2; i++)
+ finalCmd2[i - addrSize1 + 1] = (u8)((address >>
+ ((addrSize1 +
+ addrSize2 - 1 -
+ i) * 8)) & 0xFF);
+
+ if (fts_write_heap(finalCmd1, 1 + addrSize1) < OK) {
+ pr_err("%s: first write error... ERROR %08X\n",
+ __func__, ERROR_BUS_W);
+ return ERROR_BUS_W;
+ }
+
+ if (hasDummyByte == 1) {
+ if (fts_writeRead_heap(finalCmd2, 1 + addrSize2, buff,
+ toRead + 1) < OK) {
+ pr_err("%s: read error... ERROR %08X\n",
+ __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+ memcpy(outBuf, buff + 1, toRead);
+ } else {
+ if (fts_writeRead_heap(finalCmd2, 1 + addrSize2, buff,
+ toRead) < OK) {
+ pr_err("%s: read error... ERROR %08X\n",
+ __func__, ERROR_BUS_WR);
+ return ERROR_BUS_WR;
+ }
+ memcpy(outBuf, buff, toRead);
+ }
+
+ address += toRead;
+
+ outBuf += toRead;
+ }
+
+ return OK;
+}
diff --git a/fts_lib/ftsIO.h b/fts_lib/ftsIO.h
new file mode 100644
index 0000000..08de4d4
--- /dev/null
+++ b/fts_lib/ftsIO.h
@@ -0,0 +1,80 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * I2C/SPI Communication *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+/*!
+ * \file ftsIO.h
+ * \brief Contains all the definitions and prototypes used and implemented in
+ * ftsIO.c
+ */
+
+#ifndef FTS_IO_H
+#define FTS_IO_H
+
+#include "ftsSoftware.h"
+
+#define I2C_RETRY 3 /* /< number of retry in case of i2c
+ * failure */
+#define I2C_WAIT_BEFORE_RETRY 2 /* /< wait in ms before retry an i2c
+ * transaction */
+
+#ifdef I2C_INTERFACE
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+struct i2c_client *getClient(void);
+#else
+#include <linux/spi/spi.h>
+struct spi_device *getClient(void);
+#endif
+
+
+
+int openChannel(void *clt);
+struct device *getDev(void);
+
+
+
+/*************** NEW I2C API ****************/
+#ifdef I2C_INTERFACE
+int changeSAD(u8 sad);
+#endif
+int fts_read(u8 *outBuf, int byteToRead);
+int fts_read_heap(u8 *outBuf, int byteToRead);
+int fts_writeRead(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead);
+int fts_writeRead_heap(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead);
+int fts_write(u8 *cmd, int cmdLength);
+int fts_write_heap(u8 *cmd, int cmdLength);
+int fts_writeFwCmd(u8 *cmd, int cmdLength);
+int fts_writeFwCmd_heap(u8 *cmd, int cmdLength);
+int fts_writeThenWriteRead(u8 *writeCmd1, int writeCmdLength,
+ u8 *readCmd1, int readCmdLength,
+ u8 *outBuf, int byteToRead);
+int fts_writeThenWriteRead_heap(u8 *writeCmd1, int writeCmdLength,
+ u8 *readCmd1, int readCmdLength,
+ u8 *outBuf, int byteToRead);
+
+/* chunked version of fts_write */
+int fts_writeU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *data,
+ int dataSize);
+/* chunked version of fts_writeRead */
+int fts_writeReadU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *outBuf,
+ int byteToRead, int hasDummyByte);
+/* chunked, write followed by another write */
+int fts_writeU8UXthenWriteU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2,
+ AddrSize addrSize2, u64 address, u8 *data,
+ int dataSize);
+/* chunked, write followed by a writeRead */
+int fts_writeU8UXthenWriteReadU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2,
+ AddrSize addrSize2, u64 address, u8 *outBuf,
+ int count, int hasDummyByte);
+#endif
diff --git a/fts_lib/ftsSoftware.h b/fts_lib/ftsSoftware.h
new file mode 100644
index 0000000..6aa9e18
--- /dev/null
+++ b/fts_lib/ftsSoftware.h
@@ -0,0 +1,557 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FW related data *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsSoftware.h
+ * \brief Contains all the definitions and information related to the IC
+ * from a fw/driver point of view
+ */
+
+
+#ifndef FTS_SOFTWARE_H
+#define FTS_SOFTWARE_H
+#include <linux/types.h>
+#include "ftsHardware.h"
+
+#define SUPPORT_PROX_PALM
+
+/* signed type */
+typedef signed char i8; /* /< basic type that represent one signed byte (or 8
+ * bits) */
+
+/**
+ * Enumerator which contains all the possible address length expressed in
+ *bytes.
+ */
+typedef enum {
+ NO_ADDR = 0,
+ BITS_8 = 1,
+ BITS_16 = 2,
+ BITS_24 = 3,
+ BITS_32 = 4,
+ BITS_40 = 5,
+ BITS_48 = 6,
+ BITS_56 = 7,
+ BITS_64 = 8
+} AddrSize;
+
+
+/******************** NEW API *********************/
+
+/* HOST COMMAND */
+/** @defgroup host_command Fw Host op codes
+ * Valid op codes for fw commands
+ * @{
+ */
+
+/** @defgroup scan_mode Scan Mode
+ * @ingroup host_command
+ * Set the scanning mode required according to the parameters
+ * @{
+ */
+#define FTS_CMD_SCAN_MODE 0xA0 /* /< OP Code to set scan mode */
+/** @} */
+
+/** @defgroup feat_sel Feature Select
+ * @ingroup host_command
+ * Set the system defined features to enable/disable according the parameters
+ * @{
+ */
+#define FTS_CMD_FEATURE 0xA2 /* /< OP code to set features */
+/** @} */
+
+/** @defgroup sys_cmd System Command
+ * @ingroup host_command
+ * Execute a system command to perform core tasks
+ * @{
+ */
+#define FTS_CMD_SYSTEM 0xA4 /* /< OP code to write s system command
+ * */
+/** @} */
+
+/** @} */ /* end host_command group */
+
+/* SCAN MODE OPTION (0xA0) */
+/* Scan mode selection */
+/** @defgroup scan_opt Scan Mode Option
+ * @ingroup scan_mode
+ * Valid scanning modes and their options
+ * @{
+ */
+#define SCAN_MODE_ACTIVE 0x00 /* /< Select the Active scanning mode */
+#define SCAN_MODE_LOW_POWER 0x01 /* /< Select the low power scanning mode
+ * */
+#define SCAN_MODE_JIG_1 0x02 /* /< Select the Jig test 1 */
+#define SCAN_MODE_LOCKED 0x03 /* /< Select the Scan mode which will be
+ * locked */
+/** @}*/
+
+/* Active mode option (bitmask) */
+/** @defgroup active_bitmask Active Mode Bitmask
+ * @ingroup scan_opt
+ * Bitmask to use to enables the specific scanning with the SCAN_MODE_ACTIVE
+ * option
+ * @{
+ */
+#define ACTIVE_MULTI_TOUCH 0x01 /* /< Bit 0 MS/SS scan */
+#define ACTIVE_KEY 0x02 /* /< Bit 1 Key scan */
+#define ACTIVE_HOVER 0x04 /* /< Bit 2 Hover scan */
+#define ACTIVE_PROXIMITY 0x08 /* /< Bit 3 Proximity scan */
+#define ACTIVE_FORCE 0x10 /* /< Bit 4 Force scan */
+/** @}*/
+
+/* Locked mode option (locked mode) */
+/** @defgroup locked_opt Locked Mode Option
+ * @ingroup scan_opt
+ * Options to enable and lock specific scanning with the SCAN_MODE_LOCKED
+ * option
+ * @{
+ */
+#define LOCKED_ACTIVE 0x00 /* /< Active Scan Mode */
+#define LOCKED_HOVER 0x01 /* /< Hover Scan Mode */
+#define LOCKED_IDLE 0x02 /* /< Idle Scan Mode */
+#define LOCKED_ONLY_SELF 0x03 /* /< Only Self Sense scan mode */
+#define LOCKED_ONLY_MUTUAL_0 0x04 /* /< Only Mutual Sense scan mode at
+ * lowest freq */
+#define LOCKED_ONLY_MUTUAL_1 0x05 /* /< Only Mutual Sense scan mode */
+#define LOCKED_ONLY_MUTUAL_2 0x06 /* /< Only Mutual Sense scan mode */
+#define LOCKED_ONLY_MUTUAL_3 0x07 /* /< Only Mutual Sense scan mode at
+ * highest freq */
+#define LOCKED_SINGLE_ENDED_ONLY_MUTUAL_0 0x20 /* /< Only Mutual Sense
+ * scan mode at lowest
+ * freq in single ended
+ * mode
+ */
+#define LOCKED_LP_DETECT 0x10 /* /< Low Power SS */
+#define LOCKED_LP_ACTIVE 0x11 /* /< Low Power MS */
+/** @}*/
+
+/* FEATURE SELECT OPTION (0xA2) */
+/* Feature Selection */
+/** @defgroup feat_opt Feature Selection Option
+ * @ingroup feat_sel
+ * System defined features that can be enable/disable
+ * @{
+ */
+#define FEAT_SEL_GLOVE 0x00 /* /< Glove Mode */
+#define FEAT_SEL_COVER 0x01 /* /< Cover Mode */
+#define FEAT_SEL_CHARGER 0x02 /* /< Charger Mode */
+#define FEAT_SEL_GESTURE 0x03 /* /< Gesture Mode */
+#define FEAT_SEL_GRIP 0x04 /* /< Grip Detection */
+#define FEAT_SEL_STYLUS 0x07 /* /< Stylus Mode
+ * (this is a driver define, not
+ * available in FW) */
+/** @}*/
+
+/* Feature Settings */
+#define FEAT_ENABLE 1 /* /< General value to enable a feature
+ * */
+#define FEAT_DISABLE 0 /* /< General value to disable a feature
+ * */
+
+
+/* Charger */
+/** @defgroup charger_opt Charger Mode Option
+ * @ingroup feat_sel
+ * Option for Charger Mode, it is a bitmask where the each bit indicate a
+ * different kind of chager
+ * @{
+ */
+#define CHARGER_CABLE 0x01 /* /< normal usb charger */
+#define CHARGER_WIRLESS 0x02 /* /< wireless charger */
+/** @}*/
+
+/* Gestures */
+/** @defgroup gesture_opt Gesture Mode Option
+ * @ingroup feat_sel
+ * Gesture IDs of the predefined gesture recognized by the fw.
+ * The ID represent also the position of the corresponding bit in the gesture
+ * mask
+ * @{
+ */
+#define GEST_ID_UP_1F 0x01 /* /< Bottom to Top line */
+#define GEST_ID_DOWN_1F 0x02 /* /< Top to bottom line */
+#define GEST_ID_LEFT_1F 0x03 /* /< Right to left line */
+#define GEST_ID_RIGHT_1F 0x04 /* /< Left to right line */
+#define GEST_ID_DBLTAP 0x05 /* /< Double Tap */
+#define GEST_ID_O 0x06 /* /< 'O' */
+#define GEST_ID_C 0x07 /* /< 'C' */
+#define GEST_ID_M 0x08 /* /< 'M' */
+#define GEST_ID_W 0x09 /* /< 'W' */
+#define GEST_ID_E 0x0A /* /< 'e' */
+#define GEST_ID_L 0x0B /* /< 'L' */
+#define GEST_ID_F 0x0C /* /< 'F' */
+#define GEST_ID_V 0x0D /* /< 'V' */
+#define GEST_ID_AT 0x0E /* /< '@' */
+#define GEST_ID_S 0x0F /* /< 'S' */
+#define GEST_ID_Z 0x10 /* /< 'Z' */
+#define GEST_ID_LEFTBRACE 0x11 /* /< '<' */
+#define GEST_ID_RIGHTBRACE 0x12 /* /< '>' */
+#define GEST_ID_CARET 0x13 /* /< '^' */
+/** @}*/
+
+/* WRYTE SYSTEM COMMAND (0xA4) */
+/* System command */
+/** @defgroup sys_opt System Command Option
+ * @ingroup sys_cmd
+ * Valid System Command Parameters
+ * @{
+ */
+#define SYS_CMD_SPECIAL 0x00 /* /< Special Commands */
+#define SYS_CMD_INT 0x01 /* /< FW Interrupt Control */
+#define SYS_CMD_FORCE_CAL 0x02 /* /< Force Calibration */
+#define SYS_CMD_CX_TUNING 0x03 /* /< CX initialization */
+#define SYS_CMD_ITO 0x04 /* /< ITO test */
+#define SYS_CMD_SAVE_FLASH 0x05 /* /< Saving to flash */
+#define SYS_CMD_LOAD_DATA 0x06 /* /< Load Host data memory */
+#define SYS_CMD_SPECIAL_TUNING 0x08 /* /< Perform some special tuning */
+#define SYS_CMD_MP_FLAG 0x0C /* /< Update value of MP flag in RAM */
+/** @} */
+
+/* System command settings */
+/* Special commands */
+/** @defgroup sys_special_opt Special Command Option
+ * @ingroup sys_cmd
+ * Valid special command
+ * @{
+ */
+#define SPECIAL_SYS_RESET 0x00 /* /< System Reset triggered by
+ * the FW */
+#define SPECIAL_FIFO_FLUSH 0x01 /* /< Flush of the FIFO */
+#define SPECIAL_PANEL_INIT 0x02 /* /< Panel Initialization */
+#define SPECIAL_FULL_PANEL_INIT 0x03 /* /< Full panel initialization
+ * */
+#define SPECIAL_WRITE_HOST_MEM_TO_FLASH 0x04 /* /< Write */
+/** @} */
+
+
+/* Force Cal and Cx auto tuning */
+/** @defgroup forcecal_cx_opt Force Cal and Tuning Option
+ * @ingroup sys_cmd
+ * Valid bitmask for triggering forcecal or performing manual autotune
+ * @{
+ */
+#define CAL_MS_TOUCH 0x01 /* /< Mutual Sense Touch */
+#define CAL_MS_LOW_POWER 0x02 /* /< Mutual Sense Touch in low power
+ * mode */
+#define CAL_SS_TOUCH 0x04 /* /< Self Sense Touch */
+#define CAL_SS_IDLE 0x08 /* /< Self Sense Touch in idle mode */
+#define CAL_MS_KEY 0x10 /* /< Mutual Sense Key */
+#define CAL_SS_KEY 0x20 /* /< Self Sense Key */
+#define CAL_MS_FORCE 0x40 /* /< Mutual Sense Force */
+#define CAL_SS_FORCE 0x80 /* /< Self Sense Force */
+/** @} */
+
+/* ITO checks (position of the bit in the mask) */
+/** @defgroup ito_opt ITO Test Option
+ * @ingroup sys_cmd
+ * Valid option for the ITO test
+ * @{
+ */
+#define ITO_FORCE_OPEN 0x00 /* /< Check if some force channels is
+ * open */
+#define ITO_SENSE_OPEN 0x01 /* /< Check if some sense channels is
+ * open */
+#define ITO_FORCE_GROUND 0x02 /* /< Check if some force channels is
+ * short to ground */
+#define ITO_SENSE_GROUND 0x03 /* /< Check if some sense channels is
+ * short to ground */
+#define ITO_FORCE_VDD 0x04 /* /< Check if some force channels is
+ * short to VDD */
+#define ITO_SENSE_VDD 0x05 /* /< Check if some sense channels is
+ * short to VDD */
+#define ITO_FORCE_FORCE 0x06 /* /< Check force to force channels */
+#define ITO_FORCE_SENSE 0x07 /* /< Check force to sense channels */
+#define ITO_SENSE_SENSE 0x08 /* /< Check sense to sense channels */
+#define ITO_KEY_FORCE_OPEN 0x09 /* /< Check if some force channels used
+ * for the key is open */
+#define ITO_KEY_SENSE_OPEN 0x0A /* /< Check if some sense channels used
+ * for the key is open */
+/** @}*/
+
+/* Save flash */
+/** @defgroup save_opt Save to Flash Option
+ * @ingroup sys_cmd
+ * Valid option for saving data to the Flash
+ * @{
+ */
+#define SAVE_FW_CONF 0x01 /* /< Save the confing to the flash */
+#define SAVE_CX 0x02 /* /< Save the CX to the flash */
+#define SAVE_PANEL_CONF 0x04 /* /< Save the Panel configuration to the flash
+ * */
+/** @}*/
+
+/* Load Data */
+/** @defgroup load_opt Load Host Data Option
+ * @ingroup sys_cmd
+ * Valid option to ask to the FW to load host data into the memory
+ * @{
+ */
+#define LOAD_SYS_INFO 0x01 /* /< Load System Info */
+#define LOAD_CX_MS_TOUCH 0x10 /* /< Load MS Init Data for
+ * Active Mode */
+#define LOAD_CX_MS_LOW_POWER 0x11 /* /< Load MS Init Data for Low
+ * Power Mode */
+#define LOAD_CX_SS_TOUCH 0x12 /* /< Load SS Init Data for
+ * Active Mode */
+#define LOAD_CX_SS_TOUCH_IDLE 0x13 /* /< Load SS Init Data for Low
+ * Power Mode */
+#define LOAD_CX_MS_KEY 0x14 /* /< Load MS Init Data for Key
+ * */
+#define LOAD_CX_SS_KEY 0x15 /* /< Load SS Init Data for Key
+ * */
+#define LOAD_CX_MS_FORCE 0x16 /* /< Load MS Init Data for
+ * Force */
+#define LOAD_CX_SS_FORCE 0x17 /* /< Load SS Init Data for
+ * Force */
+#define LOAD_SYNC_FRAME_RAW 0x30 /* /< Load a Synchronized Raw
+ * Frame */
+#define LOAD_SYNC_FRAME_FILTER 0x31 /* /< Load a Synchronized Filter
+ * Frame */
+#define LOAD_SYNC_FRAME_STRENGTH 0x33 /* /< Load a Synchronized
+ * Strength Frame */
+#define LOAD_SYNC_FRAME_BASELINE 0x32 /* /< Load a Synchronized
+ * Baseline Frame */
+#define LOAD_PANEL_CX_TOT_MS_TOUCH 0x50 /* /< Load TOT MS Init Data for
+ * Active Mode */
+#define LOAD_PANEL_CX_TOT_MS_LOW_POWER 0x51 /* /< Load TOT MS Init Data for
+ * Low Power Mode */
+#define LOAD_PANEL_CX_TOT_SS_TOUCH 0x52 /* /< Load TOT SS Init Data for
+ * Active Mode */
+#define LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE 0x53 /* /< Load TOT SS Init Data for
+ * Low Power Mode */
+#define LOAD_PANEL_CX_TOT_MS_KEY 0x54 /* /< Load TOT MS Init Data for
+ * Key */
+#define LOAD_PANEL_CX_TOT_SS_KEY 0x55 /* /< Load TOT SS Init Data for
+ * Key */
+#define LOAD_PANEL_CX_TOT_MS_FORCE 0x56 /* /< Load TOT MS Init Data for
+ * Force */
+#define LOAD_PANEL_CX_TOT_SS_FORCE 0x57 /* /< Load TOT SS Init Data for
+ * Force */
+#define LOAD_SENS_CAL_COEFF 0xC0 /* /< Load Sesitivity
+ * Calibration Coefficients */
+#define LOAD_GOLDEN_MUTUAL_RAW 0x80 /* /< Load Goden Mutual Raw Data */
+
+/** @}*/
+
+/* Special Tuning */
+/** @defgroup spcl_tun_opt Special Tuning Option
+ * @ingroup sys_cmd
+ * Valid special tuning operations which the fw can perform (bitmask)
+ * @{
+ */
+#define SPECIAL_TUNING_LP_TIMER 0x01 /* /< Perform LP Timer calibration */
+#define SPECIAL_TUNING_IOFF 0x02 /* /< Perform Ioff calibration */
+
+/** @}*/
+
+/* EVENT ID */
+/** @defgroup events_group FW Event IDs and Types
+ * Event IDs and Types pushed by the FW into the FIFO
+ * @{
+ */
+#define EVT_ID_NOEVENT 0x00 /* /< No Events */
+#define EVT_ID_CONTROLLER_READY 0x03 /* /< Controller ready, issued after a
+ * system reset. */
+#define EVT_ID_ENTER_POINT 0x13 /* /< Touch enter in the sensing area */
+#define EVT_ID_MOTION_POINT 0x23 /* /< Touch motion (a specific touch
+ * changed position) */
+#define EVT_ID_LEAVE_POINT 0x33 /* /< Touch leave the sensing area */
+#define EVT_ID_STATUS_UPDATE 0x43 /* /< FW report a system condition
+ * change */
+#define EVT_ID_USER_REPORT 0x53 /* /< User related events triggered
+ * (keys, gestures, proximity etc) */
+#define EVT_ID_DEBUG 0xE3 /* /< Debug Info */
+#define EVT_ID_ERROR 0xF3 /* /< Error Event */
+
+/* /< Max number of unique event IDs supported */
+#define NUM_EVT_ID (((EVT_ID_ERROR & 0xF0) >> 4) + 1)
+
+/** @}*/
+
+/* STATUS TYPE */
+/** @defgroup status_type Status Event Types
+ * @ingroup events_group
+ * Types of EVT_ID_STATUS_UPDATE events
+ * @{
+ */
+#define EVT_TYPE_STATUS_ECHO 0x01 /* /< Echo event,
+ * contain the first 5 bytes of
+ * the FW command sent */
+#define EVT_TYPE_STATUS_GPIO_CHAR_DET 0x02 /*/< Gpio Charger detected */
+#define EVT_TYPE_STATUS_FRAME_DROP 0x03 /* /< Some frame was skipped
+ * during the elaboration */
+#define EVT_TYPE_STATUS_FORCE_CAL 0x05 /* /< Force Calibration has
+ * triggered */
+#define EVT_TYPE_STATUS_WATER 0x06 /* /< Water Mode */
+#define EVT_TYPE_STATUS_SS_RAW_SAT 0x07 /* /< Self Sense data saturated */
+#define EVT_TYPE_STATUS_PRE_WAT_DET 0x08 /* /< Previous Water Detect* */
+#define EVT_TYPE_STATUS_NOISE 0x09 /* /< Noise Status* */
+#define EVT_TYPE_STATUS_STIMPAD 0x0A /* /< Stimpad Status* */
+#define EVT_TYPE_STATUS_NO_TOUCH 0x0B /* /< No Touch Status* */
+#define EVT_TYPE_STATUS_IDLE 0x0C /* /< Idle Status* */
+#define EVT_TYPE_STATUS_PALM_TOUCH 0x0D /* /< Palm Touch Status* */
+#define EVT_TYPE_STATUS_GRIP_TOUCH 0x0E /* /< Grip Touch Status* */
+#define EVT_TYPE_STATUS_GOLDEN_RAW_VAL 0x0F /* /< Golden Raw
+ * Validation Status */
+#define EVT_TYPE_STATUS_GOLDEN_RAW_ERR 0x16 /* /< Golden Raw
+ * Data Abnormal */
+#ifdef SUPPORT_PROX_PALM
+#define EVT_TYPE_STATUS_PROX_PALM 0x18 /* /< Proximity: Palm**/
+#endif
+
+/** @} */
+
+/* USER TYPE */
+/** @defgroup user_type User Event Types
+ * @ingroup events_group
+ * Types of EVT_ID_USER_REPORT events generated by thw FW
+ * @{
+ */
+#define EVT_TYPE_USER_KEY 0x00 /* /< Keys pressed/relesed event report
+ * */
+#define EVT_TYPE_USER_PROXIMITY 0x01 /* /< Proximity detection event report
+ * */
+#define EVT_TYPE_USER_GESTURE 0x02 /* /< Gesture detection event report */
+/** @}*/
+
+/* ERROR TYPE */
+/** @defgroup error_type Error Event Types
+ * @ingroup events_group
+ * Types of EVT_ID_ERROR events reported by the FW
+ * @{
+ */
+#define EVT_TYPE_ERROR_HARD_FAULT 0x02 /* /< Hard Fault */
+#define EVT_TYPE_ERROR_WATCHDOG 0x06 /* /< Watchdog timer expired */
+
+#define EVT_TYPE_ERROR_CRC_CFG_HEAD 0x20 /* /< CRC error in the Config
+ * Area Header */
+#define EVT_TYPE_ERROR_CRC_CFG 0x21 /* /< CRC error in the Config
+ * Area */
+#define EVT_TYPE_ERROR_CRC_PANEL_HEAD 0x22 /* /< CRC error in the Panel
+ * Area Header */
+#define EVT_TYPE_ERROR_CRC_PANEL 0x23 /* /< CRC error in the Panel
+ * Area */
+
+#define EVT_TYPE_ERROR_ITO_FORCETOGND 0x60 /* /< Force channel/s short to
+ * ground */
+#define EVT_TYPE_ERROR_ITO_SENSETOGND 0x61 /* /< Sense channel/s short to
+ * ground */
+#define EVT_TYPE_ERROR_ITO_FORCETOVDD 0x62 /* /< Force channel/s short to
+ * VDD */
+#define EVT_TYPE_ERROR_ITO_SENSETOVDD 0x63 /* /< Sense channel/s short to
+ * VDD */
+#define EVT_TYPE_ERROR_ITO_FORCE_P2P 0x64 /* /< Pin to Pin short Force
+ * channel/s */
+#define EVT_TYPE_ERROR_ITO_SENSE_P2P 0x65 /* /< Pin to Pin short Sense
+ * channel/s */
+#define EVT_TYPE_ERROR_ITO_FORCEOPEN 0x66 /* /< Force Panel open */
+#define EVT_TYPE_ERROR_ITO_SENSEOPEN 0x67 /* /< Sense Panel open */
+#define EVT_TYPE_ERROR_ITO_KEYOPEN 0x68 /* /< Key open */
+
+#define EVT_TYPE_ERROR_CRC_CX_HEAD 0xA0 /* /< CRC error in the CX Area
+ * Header */
+#define EVT_TYPE_ERROR_CRC_CX 0xA1 /* /< CRC error in the CX Area
+ * */
+#define EVT_TYPE_ERROR_FLASH_FAILED 0xA4 /* Flash error, cause unknown */
+#define EVT_TYPE_ERROR_CRC_CX_SUB_HEAD 0xA5 /* /< CRC error in the CX
+ * Subsection Area Header */
+#define EVT_TYPE_ERROR_CRC_CX_SUB 0xA6 /* /< CRC error in the CX
+ * Subsection Area */
+
+#define EVT_TYPE_ERROR_ESD 0xF0 /* /< ESD error */
+
+#define EVT_TYPE_ERROR_OSC_TRIM 0x24 /* /< OSC Trim error */
+#define EVT_TYPE_ERROR_AOFFSET_TRIM 0x29 /* /< Aoffset Trim error */
+/** @}*/
+
+
+/** @defgroup address Chip Address
+ * Collection of HW and SW Addresses useful to collect different kind of data
+ * @{
+ */
+
+/** @defgroup config_adr SW Address
+ * @ingroup address
+ * Important addresses of data stored into Config memory (and sometimes their
+ * dimensions)
+ * @{
+ */
+#define ADDR_CONFIG_ID 0x0010 /* /< Starting Address of the config ID
+ * */
+#define CONFIG_ID_BYTE 2 /* /< Number of bytes of config ID */
+#define ADDR_CONFIG_SENSE_LEN 0x0030 /* /< Address where is stored the number
+ * of sense channels */
+#define ADDR_CONFIG_FORCE_LEN 0x0031 /* /< Address where is stored the number
+ * of force channels */
+#define ADDR_CONFIG_AUTOCAL 0x0040 /* /< Address where is stored the Auto
+ * Calibration register */
+#define ADDR_CONFIG_T_CYCLE 0x00ED /* /< Address where is stored the MS T
+ * cycle */
+#define ADDR_CONFIG_MNM 0x01F0 /* /< Address where is stored the MNM
+ * register */
+#define ADDR_CONFIG_MRN 0x01F1 /* /< Address where is stored the MRN
+ * register */
+#define ADDR_CONFIG_R0_CYCLE 0x01F2 /* /< Address where is stored the first
+ * R cycle */
+/** @}*/
+
+/** @}*/
+
+/* @defgroup mp_flags MP Flags value
+ * @ingroup mp_test
+ * Specify the MP flags value which are written into the flash after performing
+ * a full panel initialization which pass all the tests.
+ * @{
+ */
+
+#define MP_FLAG_FACTORY 0xA5 /* /< Full Panel Init done in factory */
+#define MP_FLAG_BOOT 0x5A /* /< Full Panel Init done at boot */
+#define MP_FLAG_OTHERS 0xFF /* /< Full Panel Init done somewhere
+ * else
+ */
+
+/** @}*/
+
+/** @}*/
+
+/* ERROR INFO */
+#define ERROR_DUMP_ROW_SIZE 32 /* /< number of rows of the error memory
+ * */
+#define ERROR_DUMP_COL_SIZE 4 /* /< number of bytes for each row of
+ * the error memory */
+#define ERROR_DUMP_SIGNATURE 0xFA5005AF /* /< first row signature of a
+ * proper dump */
+
+/* Touch Types */
+#define TOUCH_TYPE_INVALID 0x00 /* /< Invalid touch type */
+#define TOUCH_TYPE_FINGER 0x01 /* /< Finger touch */
+#define TOUCH_TYPE_GLOVE 0x02 /* /< Glove touch */
+#define TOUCH_TYPE_STYLUS 0x03 /* /< Stylus touch */
+#define TOUCH_TYPE_PALM 0x04 /* /< Palm touch */
+#define TOUCH_TYPE_HOVER 0x00 /* /< Hovering touch */
+#define TOUCH_TYPE_GRIP 0x07 /* /< Hovering touch */
+
+/* Keys code */
+#define FTS_KEY_0 0x01 /* /< Key 0 bit */
+#define FTS_KEY_1 0x02 /* /< Key 1 bit */
+#define FTS_KEY_2 0x04 /* /< Key 2 bit */
+#define FTS_KEY_3 0x08 /* /< Key 3 bit */
+#define FTS_KEY_4 0x10 /* /< Key 4 bit */
+#define FTS_KEY_5 0x20 /* /< Key 5 bit */
+#define FTS_KEY_6 0x40 /* /< Key 6 bit */
+#define FTS_KEY_7 0x80 /* /< Key 7 bit */
+
+#endif
diff --git a/fts_lib/ftsTest.c b/fts_lib/ftsTest.c
new file mode 100644
index 0000000..0e1a15f
--- /dev/null
+++ b/fts_lib/ftsTest.c
@@ -0,0 +1,7770 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS API for MP test *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTest.c
+ * \brief Contains all the functions related to the Mass Production Test
+ */
+
+#include "ftsCompensation.h"
+#include "ftsCore.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"
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/serio.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/firmware.h>
+
+
+#ifdef LIMITS_H_FILE
+#include "../fts_limits.h"
+#endif
+
+
+TestToDo tests; /* /< global variable that specify the tests to perform during
+ * the Mass Production Test */
+static LimitFile limit_file; /* /< variable which contains the limit file
+ * during test */
+
+/**
+ * Initialize the testToDo variable with the default tests to perform during
+ * the Mass Production Test
+ * @return OK
+ */
+int initTestToDo(void)
+{
+ /*** Initialize Limit File ***/
+ limit_file.size = 0;
+ limit_file.data = NULL;
+ strlcpy(limit_file.name, " ", MAX_LIMIT_FILE_NAME);
+
+#ifndef COMPUTE_INIT_METHOD
+ tests.MutualRawAdjITO = 1;
+ tests.MutualRawMapITO = 1;
+
+ tests.MutualRaw = 0;
+ tests.MutualRawMap = 1;
+ tests.MutualRawGap = 0;
+ tests.MutualRawAdj = 0;
+ tests.MutualRawAdjGap = 0;
+ tests.MutualRawAdjPeak = 0;
+
+ tests.MutualRawLP = 0;
+ tests.MutualRawGapLP = 0;
+ tests.MutualRawMapLP = 1;
+ tests.MutualRawAdjLP = 0;
+
+ tests.MutualCx1 = 0;
+ tests.MutualCx2 = 0;
+ tests.MutualCx2Adj = 0;
+ tests.MutualCxTotal = 0;
+ tests.MutualCxTotalAdj = 0;
+
+ tests.MutualCx1LP = 0;
+ tests.MutualCx2LP = 0;
+ tests.MutualCx2AdjLP = 0;
+ tests.MutualCxTotalLP = 0;
+ tests.MutualCxTotalAdjLP = 0;
+
+#ifdef PHONE_KEY
+ tests.MutualKeyRaw = 0;
+#else
+ tests.MutualKeyRaw = 0;
+#endif
+ tests.MutualKeyCx1 = 0;
+ tests.MutualKeyCx2 = 0;
+#ifdef PHONE_KEY
+ tests.MutualKeyCxTotal = 0;
+#else
+ tests.MutualKeyCxTotal = 0;
+#endif
+
+ tests.SelfForceRaw = 0;
+ tests.SelfForceRawGap = 0;
+ tests.SelfForceRawMap = 1;
+
+ tests.SelfForceRawLP = 0;
+ tests.SelfForceRawGapLP = 0;
+ tests.SelfForceRawMapLP = 1;
+
+ tests.SelfForceIx1 = 0;
+ tests.SelfForceIx2 = 0;
+ tests.SelfForceIx2Adj = 0;
+ tests.SelfForceIxTotal = 0;
+ tests.SelfForceIxTotalAdj = 0;
+ tests.SelfForceCx1 = 0;
+ tests.SelfForceCx2 = 0;
+ tests.SelfForceCx2Adj = 0;
+ tests.SelfForceCxTotal = 0;
+ tests.SelfForceCxTotalAdj = 0;
+ tests.SelfForceIx1LP = 0;
+ tests.SelfForceIx2LP = 0;
+ tests.SelfForceIx2AdjLP = 0;
+ tests.SelfForceIxTotalLP = 0;
+ tests.SelfForceIxTotalAdjLP = 0;
+ tests.SelfForceCx1LP = 0;
+ tests.SelfForceCx2LP = 0;
+ tests.SelfForceCx2AdjLP = 0;
+ tests.SelfForceCxTotalLP = 0;
+ tests.SelfForceCxTotalAdjLP = 0;
+
+ tests.SelfSenseRaw = 0;
+ tests.SelfSenseRawGap = 0;
+ tests.SelfSenseRawMap = 1;
+
+ tests.SelfSenseRawLP = 0;
+ tests.SelfSenseRawGapLP = 0;
+ tests.SelfSenseRawMapLP = 1;
+
+ tests.SelfSenseIx1 = 0;
+ tests.SelfSenseIx2 = 0;
+ tests.SelfSenseIx2Adj = 0;
+ tests.SelfSenseIxTotal = 0;
+ tests.SelfSenseIxTotalAdj = 0;
+ tests.SelfSenseCx1 = 0;
+ tests.SelfSenseCx2 = 0;
+ tests.SelfSenseCx2Adj = 0;
+ tests.SelfSenseCxTotal = 0;
+ tests.SelfSenseCxTotalAdj = 0;
+ tests.SelfSenseIx1LP = 0;
+ tests.SelfSenseIx2LP = 0;
+ tests.SelfSenseIx2AdjLP = 0;
+ tests.SelfSenseIxTotalLP = 0;
+ tests.SelfSenseIxTotalAdjLP = 0;
+ tests.SelfSenseCx1LP = 0;
+ tests.SelfSenseCx2LP = 0;
+ tests.SelfSenseCx2AdjLP = 0;
+ tests.SelfSenseCxTotalLP = 0;
+ tests.SelfSenseCxTotalAdjLP = 0;
+#else
+ tests.MutualRawAdjITO = 1;
+ tests.MutualRawMapITO = 1;
+
+ tests.MutualRaw = 1; /* in case of YOCTA please use Map */
+ tests.MutualRawMap = 0;
+ tests.MutualRawGap = 0;
+ tests.MutualRawAdj = 0;
+ tests.MutualRawAdjGap = 0;
+ tests.MutualRawAdjPeak = 0;
+
+ tests.MutualRawLP = 0; /* in case of YOCTA please use Map */
+ tests.MutualRawGapLP = 0;
+ tests.MutualRawMapLP = 0;
+ tests.MutualRawAdjLP = 0;
+
+ tests.MutualCx1 = 0;
+ tests.MutualCx2 = 0;
+ tests.MutualCx2Adj = 0;
+ tests.MutualCxTotal = 0;
+ tests.MutualCxTotalAdj = 0;
+
+ tests.MutualCx1LP = 0;
+ tests.MutualCx2LP = 1;
+ tests.MutualCx2AdjLP = 1;
+ tests.MutualCxTotalLP = 0;
+ tests.MutualCxTotalAdjLP = 0;
+
+#ifdef PHONE_KEY
+ tests.MutualKeyRaw = 0;
+#else
+ tests.MutualKeyRaw = 0;
+#endif
+ tests.MutualKeyCx1 = 0;
+ tests.MutualKeyCx2 = 0;
+#ifdef PHONE_KEY
+ tests.MutualKeyCxTotal = 0;
+#else
+ tests.MutualKeyCxTotal = 0;
+#endif
+
+ tests.SelfForceRaw = 1; /* in case of YOCTA please use Map */
+ tests.SelfForceRawGap = 0;
+ tests.SelfForceRawMap = 0;
+
+ tests.SelfForceRawLP = 1; /* in case of YOCTA please use Map */
+ tests.SelfForceRawGapLP = 0;
+ tests.SelfForceRawMapLP = 0;
+
+ tests.SelfForceIx1 = 0;
+ tests.SelfForceIx2 = 0;
+ tests.SelfForceIx2Adj = 0;
+ tests.SelfForceIxTotal = 1;
+ tests.SelfForceIxTotalAdj = 0;
+ tests.SelfForceCx1 = 0;
+ tests.SelfForceCx2 = 0;
+ tests.SelfForceCx2Adj = 0;
+ tests.SelfForceCxTotal = 0;
+ tests.SelfForceCxTotalAdj = 0;
+ tests.SelfForceIx1LP = 0;
+ tests.SelfForceIx2LP = 0;
+ tests.SelfForceIx2AdjLP = 0;
+ tests.SelfForceIxTotalLP = 1;
+ tests.SelfForceIxTotalAdjLP = 0;
+ tests.SelfForceCx1LP = 0;
+ tests.SelfForceCx2LP = 0;
+ tests.SelfForceCx2AdjLP = 0;
+ tests.SelfForceCxTotalLP = 0;
+ tests.SelfForceCxTotalAdjLP = 0;
+
+ tests.SelfSenseRaw = 1;
+ tests.SelfSenseRawGap = 0;
+ tests.SelfSenseRawMap = 0;
+
+ tests.SelfSenseRawLP = 0;
+ tests.SelfSenseRawGapLP = 0;
+ tests.SelfSenseRawMapLP = 0;
+
+ tests.SelfSenseIx1 = 0;
+ tests.SelfSenseIx2 = 0;
+ tests.SelfSenseIx2Adj = 0;
+ tests.SelfSenseIxTotal = 1;
+ tests.SelfSenseIxTotalAdj = 0;
+ tests.SelfSenseCx1 = 0;
+ tests.SelfSenseCx2 = 0;
+ tests.SelfSenseCx2Adj = 0;
+ tests.SelfSenseCxTotal = 0;
+ tests.SelfSenseCxTotalAdj = 0;
+ tests.SelfSenseIx1LP = 0;
+ tests.SelfSenseIx2LP = 0;
+ tests.SelfSenseIx2AdjLP = 0;
+ tests.SelfSenseIxTotalLP = 0;
+ tests.SelfSenseIxTotalAdjLP = 0;
+ tests.SelfSenseCx1LP = 0;
+ tests.SelfSenseCx2LP = 0;
+ tests.SelfSenseCx2AdjLP = 0;
+ tests.SelfSenseCxTotalLP = 0;
+ tests.SelfSenseCxTotalAdjLP = 0;
+#endif
+ return OK;
+}
+
+/**
+ * Compute the Horizontal adjacent matrix doing the abs of the difference
+ * between the column i with the i-1 one. \n
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of signed bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjHoriz(i8 *data, int row, int column, u8 **result)
+{
+ int i, j;
+ int size = row * (column - 1);
+
+ if (column < 2) {
+ pr_err("computeAdjHoriz: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjHoriz: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Horizontal adjacent matrix of short values doing the abs of
+ * the difference between the column i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of signed bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which
+ * will contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjHorizTotal(short *data, int row, int column, u16 **result)
+{
+ int i, j;
+ int size = row * (column - 1);
+
+ if (column < 2) {
+ pr_err("computeAdjHorizTotal: ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjHorizTotal: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Vertical adjacent matrix doing the abs of the difference between
+ * the row i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other. \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of signed bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjVert(i8 *data, int row, int column, u8 **result)
+{
+ int i, j;
+ int size = (row - 1) * (column);
+
+ if (row < 2) {
+ pr_err("computeAdjVert: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjVert: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Vertical adjacent matrix of short values doing the abs of
+ * the difference between the row i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other. \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of signed bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjVertTotal(short *data, int row, int column, u16 **result)
+{
+ int i, j;
+ int size = (row - 1) * (column);
+
+ if (row < 2) {
+ pr_err("computeAdjVertTotal: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjVertTotal: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Horizontal adjacent matrix doing the abs of the difference
+ * between the column i with the i-1 one. \n
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of unsigned bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjHorizFromU(u8 *data, int row, int column, u8 **result)
+{
+ int i, j;
+ int size = row * (column - 1);
+
+ if (column < 2) {
+ pr_err("computeAdjHoriz: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjHoriz: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Horizontal adjacent matrix of u16 values doing the abs of
+ * the difference between the column i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of unsigned bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjHorizTotalFromU(u16 *data, int row, int column, u16 **result)
+{
+ int i, j;
+ int size = row * (column - 1);
+
+ if (column < 2) {
+ pr_err("computeAdjHorizTotal: ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjHorizTotal: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Vertical adjacent matrix doing the abs of the difference between
+ * the row i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other. \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of unsigned bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjVertFromU(u8 *data, int row, int column, u8 **result)
+{
+ int i, j;
+ int size = (row - 1) * (column);
+
+ if (row < 2) {
+ pr_err("computeAdjVert: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjVert: ERROR %08X\n", 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;
+}
+
+/**
+ * Compute the Vertical adjacent matrix of u16 values doing the abs of
+ * the difference between the row i with the i-1 one.
+ * Both the original data matrix and the adj matrix are disposed as 1 dimension
+ * array one row after the other. \n
+ * The resulting matrix has one column less than the starting original one \n
+ * @param data pointer to the array of unsigned bytes containing the original
+ * data
+ * @param row number of rows of the original data
+ * @param column number of columns of the original data
+ * @param result pointer of a pointer to an array of unsigned bytes which will
+ * contain the adj matrix
+ * @return OK if success or an error code which specify the type of error
+ */
+int computeAdjVertTotalFromU(u16 *data, int row, int column, u16 **result)
+{
+ int i, j;
+ int size = (row - 1) * (column);
+
+ if (row < 2) {
+ pr_err("computeAdjVertTotal: ERROR %08X\n", ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL);
+ if (*result == NULL) {
+ pr_err("computeAdjVertTotal: ERROR %08X\n", 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;
+}
+
+
+
+/**
+ * Check that each value of a matrix of short doesn't exceed a min and a Max
+ * value
+ * (these values are included in the interval). \n
+ * The matrix is stored as 1 dimension array one row after the other. \n
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param min minimum value allowed
+ * @param max Maximum value allowed
+ * @return the number of elements that overcome the specified interval (0 = 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) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j, data[i * column + j], min, max);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that the difference between the max and min of a matrix of short
+ * is less or equal to a threshold.\n
+ * The matrix is stored as 1 dimension array one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param threshold threshold value allowed
+ * @return OK if the difference is <= to threshold otherwise
+ * ERROR_TEST_CHECK_FAIL
+ */
+int checkLimitsGap(short *data, int row, int column, int threshold)
+{
+ int i, j;
+ int min_node;
+ int max_node;
+
+ if (row == 0 || column == 0) {
+ pr_err("checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %08X\n",
+ row, 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) {
+ pr_err("checkLimitsGap: GAP = %d exceed limit %d\n",
+ max_node - min_node, threshold);
+ return ERROR_TEST_CHECK_FAIL;
+ } else
+ return OK;
+}
+
+/*
+ * Check that the difference between the max and min of a matrix of short is
+ * less or equal to a threshold.\n
+ * The matrix is stored as 1 dimension array one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param threshold threshold value allowed
+ * @param row_start index of the starting column which should be considered
+ * @param column_start index of the starting column which should be considered
+ * @param row_end number of index to subtract to row to identify last
+ * valid row to check
+ * @param column_end number of index to subtract to column to identify last
+ * valid column to check
+ * @return OK if the difference is <= to threshold otherwise
+ * ERROR_TEST_CHECK_FAIL
+ */
+int checkLimitsGapOffsets(short *data, int row, int column, int threshold,
+ int row_start, int column_start, int row_end, int column_end)
+{
+ int i, j;
+ int min_node;
+ int max_node;
+
+ if (row == 0 || column == 0) {
+ pr_err("checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %08X\n",
+ row, column, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ min_node = data[row_start*column+column_start];
+ max_node = data[row_start*column+column_start];
+
+ for (i = row_start; i < row-row_end; i++) {
+ for (j = column_start; j < column-column_end; 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) {
+ pr_err("checkLimitsGap: GAP = %d exceed limit %d\n",
+ max_node - min_node, threshold);
+ return ERROR_TEST_CHECK_FAIL;
+ } else
+ return OK;
+}
+
+/**
+ * Check that each value of a matrix of i8 doesn't exceed a specific min and
+ * Max value set for each node (these values are included in the interval). \n
+ * The matrixes of data, min and max values are stored as 1 dimension arrays
+ * one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param min pointer to a matrix which specify the minimum value allowed for
+ * each node
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+int checkLimitsMap(i8 *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]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j, data[i * column + j],
+ min[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that each value of a matrix of short doesn't exceed a specific min and
+ * Max value set for each node (these values are included in the interval).
+ * The matrixes of data, min and max values are stored as 1 dimension arrays
+ * one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param min pointer to a matrix which specify the minimum value allowed for
+ * each node
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+int checkLimitsMapTotal(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[i * column + j] ||
+ data[i * column + j] > max[i * column + j]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j, data[i * column + j],
+ min[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that each value of a matrix of u8 doesn't exceed a specific min and
+ * Max value set for each node (these values are included in the interval). \n
+ * The matrixes of data, min and max values are stored as 1 dimension arrays
+ * one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param min pointer to a matrix which specify the minimum value allowed for
+ * each node
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+int checkLimitsMapFromU(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]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j, data[i * column + j],
+ min[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that each value of a matrix of u16 doesn't exceed a specific min and
+ * Max value set for each node (these values are included in the interval).
+ * The matrixes of data, min and max values are stored as 1 dimension arrays
+ * one row after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param min pointer to a matrix which specify the minimum value allowed for
+ * each node
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+int checkLimitsMapTotalFromU(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]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j, data[i * column + j],
+ min[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that each value of a matrix of u8 doesn't exceed a specific Max value
+ * set for each node (max value is included in the interval).
+ * The matrixes of data and max values are stored as 1 dimension arrays one row
+ * after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+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]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit > %d\n",
+ __func__, i, j,
+ data[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Check that each value of a matrix of u16 doesn't exceed a specific Max value
+ * set for each node (max value is included in the interval).
+ * The matrixes of data and max values are stored as 1 dimension arrays one row
+ * after the other.
+ * @param data pointer to the array of short containing the data to check
+ * @param row number of rows of data
+ * @param column number of columns of data
+ * @param max pointer to a matrix which specify the Maximum value allowed for
+ * each node
+ * @return the number of elements that overcome the specified interval (0 = OK)
+ */
+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]) {
+ pr_info("%s: Node[%d,%d] = %d exceed limit > %d\n",
+ __func__, i, j,
+ data[i * column + j],
+ max[i * column + j]);
+ count++;
+ }
+ }
+ }
+
+ return count; /* if count is 0 = OK, test completed successfully */
+}
+
+/**
+ * Perform an ITO test setting all the possible options
+ * (see @link ito_opt ITO Options @endlink) and checking MS Raw ADJ if enabled
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @param frame pointer to a MutualSenseframe variable which will store the ITO
+ * MS RAW data. If NULL, no data will be returned while if MutualRawAdjITO
+ * item in todo is disabled the variable will be untouched
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ito(const char *path_limits, TestToDo *todo,
+ MutualSenseFrame *frame)
+{
+ int res = OK;
+ u8 sett[2] = { 0x00, 0x00 };
+ MutualSenseFrame msRawFrame;
+ MutualSenseFrame *ptr_frame = NULL;
+ int *thresholds = NULL;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ u16 *adj = NULL;
+ int trows, tcolumns;
+
+ msRawFrame.node_data = NULL;
+
+ pr_info("ITO Production test is starting...\n");
+
+ res = fts_system_reset();
+ if (res < 0) {
+ pr_err("%s: ERROR %08X\n", __func__, ERROR_PROD_TEST_ITO);
+ return res | ERROR_PROD_TEST_ITO;
+ }
+
+ sett[0] = SPECIAL_TUNING_IOFF;
+ pr_info("Trimming Ioff...\n");
+ res = writeSysCmd(SYS_CMD_SPECIAL_TUNING, sett, 2);
+ if (res < OK) {
+ pr_err("production_test_ito: Trimm Ioff ERROR %08X\n",
+ (res | ERROR_PROD_TEST_ITO));
+ return res | ERROR_PROD_TEST_ITO;
+ }
+
+ sett[0] = 0xFF;
+ sett[1] = 0x01;
+ pr_info("ITO Check command sent...\n");
+ res = writeSysCmd(SYS_CMD_ITO, sett, 2);
+ if (res < OK) {
+ pr_err("production_test_ito: ERROR %08X\n",
+ (res | ERROR_PROD_TEST_ITO));
+ return res | ERROR_PROD_TEST_ITO;
+ }
+
+ pr_info("ITO Command = OK!\n");
+
+ pr_info("MS RAW ITO ADJ TEST:\n");
+ if (todo->MutualRawAdjITO == 1 || todo->MutualRawMapITO == 1) {
+ pr_info("Collecting MS Raw data...\n");
+
+ if (frame != NULL) {
+ pr_info("%s: Copying MS Raw data to caller!\n",
+ __func__);
+ ptr_frame = frame;
+ } else
+ ptr_frame = &msRawFrame;
+
+ res |= getMSFrame3(MS_RAW, ptr_frame);
+ if (res < OK) {
+ pr_err("%s: getMSFrame failed... ERROR %08X\n",
+ __func__, ERROR_PROD_TEST_ITO);
+ goto ERROR;
+ }
+
+ print_frame_short("MS Raw ITO frame =",
+ array1dTo2d_short(
+ (*ptr_frame).node_data,
+ (*ptr_frame).node_data_size,
+ (*ptr_frame).header.sense_node),
+ (*ptr_frame).header.force_node,
+ (*ptr_frame).header.sense_node);
+
+ pr_info("MS RAW ITO ADJ HORIZONTAL TEST:\n");
+ res = computeAdjHorizTotal((*ptr_frame).node_data,
+ (*ptr_frame).header.force_node,
+ (*ptr_frame).header.sense_node,
+ &adj);
+ if (res < OK) {
+ pr_err("%s: computeAdjHoriz failed... ERROR %08X\n",
+ __func__, ERROR_PROD_TEST_ITO);
+ goto ERROR;
+ }
+
+ res = parseProductionTestLimits(path_limits, &limit_file,
+ MS_RAW_ITO_ADJH, &thresholds,
+ &trows, &tcolumns);
+ if (res < OK || (trows != (*ptr_frame).header.force_node ||
+ tcolumns != (*ptr_frame).header.sense_node -
+ 1)) {
+ pr_err("%s: parseProductionTestLimits MS_RAW_ITO_ADJH failed... ERROR %08X\n",
+ __func__, ERROR_PROD_TEST_DATA);
+ goto ERROR;
+ }
+
+
+ res = checkLimitsMapAdjTotal(adj,
+ (*ptr_frame).header.force_node,
+ (*ptr_frame).header.sense_node - 1,
+ thresholds);
+ if (res != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ITO ADJH failed... ERROR COUNT = %d\n",
+ res);
+ pr_err("MS RAW ITO ADJ HORIZONTAL TEST:.................FAIL\n\n");
+ res = ERROR_PROD_TEST_ITO;
+ goto ERROR;
+ } else
+ pr_info("MS RAW ITO ADJ HORIZONTAL TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ kfree(adj);
+ adj = NULL;
+
+ pr_info("MS RAW ITO ADJ VERTICAL TEST:\n");
+ res = computeAdjVertTotal((*ptr_frame).node_data,
+ (*ptr_frame).header.force_node,
+ (*ptr_frame).header.sense_node,
+ &adj);
+ if (res < OK) {
+ pr_err("%s: computeAdjVert failed... ERROR %08X\n",
+ __func__, ERROR_PROD_TEST_ITO);
+ goto ERROR;
+ }
+
+ res = parseProductionTestLimits(path_limits, &limit_file,
+ MS_RAW_ITO_ADJV, &thresholds,
+ &trows, &tcolumns);
+ if (res < OK || (trows != (*ptr_frame).header.force_node - 1 ||
+ tcolumns != (*ptr_frame).header.sense_node)) {
+ pr_err("%s: parseProductionTestLimits MS_RAW_ITO_ADJV failed... ERROR %08X\n",
+ __func__, ERROR_PROD_TEST_ITO);
+ goto ERROR;
+ }
+
+
+ res = checkLimitsMapAdjTotal(adj, (*ptr_frame).header.force_node
+ - 1, (*ptr_frame).header.sense_node,
+ thresholds);
+ if (res != OK) {
+ pr_err("%s: checkLimitsAdj MS RAW ITO ADJV failed... ERROR COUNT = %d\n",
+ __func__, res);
+ pr_err("MS RAW ITO ADJ VERTICAL TEST:.................FAIL\n\n");
+ res = ERROR_PROD_TEST_ITO;
+ goto ERROR;
+ } else
+ pr_info("MS RAW ITO ADJ VERTICAL TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ kfree(adj);
+ adj = NULL;
+
+ pr_info("MS RAW ITO MIN MAX TEST:\n");
+ if (todo->MutualRawMapITO == 1) {
+ res = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_ITO_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (res < OK || (trows !=
+ (*ptr_frame).header.force_node ||
+ tcolumns != (*ptr_frame).header.sense_node)) {
+ pr_err("production_test_data: parseProductionMS_RAW_ITO_EACH_NODE_MIN failed..."
+ "ERROR %08X\n", ERROR_PROD_TEST_DATA);
+ res |= ERROR_PROD_TEST_DATA;
+ goto ERROR;
+ }
+
+ res = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_ITO_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (res < OK || (trows !=
+ (*ptr_frame).header.force_node ||
+ tcolumns != (*ptr_frame).header.sense_node)) {
+ pr_err("production_test_data: parseProductionMS_RAW_ITO_EACH_NODE_MAX failed..."
+ "ERROR %08X\n", ERROR_PROD_TEST_DATA);
+ res |= ERROR_PROD_TEST_DATA;
+ goto ERROR;
+ }
+
+ res = checkLimitsMapTotal((*ptr_frame).node_data,
+ (*ptr_frame).header.force_node,
+ (*ptr_frame).header.sense_node, thresholds_min,
+ thresholds_max);
+ if (res != OK) {
+ pr_err("production_test_data: checkLimitsMinMax"
+ " MS RAW ITO failed... ERROR COUNT = %d\n",
+ res);
+ pr_err("MS RAW ITO MIN MAX TEST:.................FAIL\n\n");
+ res |= ERROR_PROD_TEST_DATA;
+ goto ERROR;
+ } else {
+ pr_info("MS RAW ITO MIN MAX TEST:................OK\n");
+ }
+ } else {
+ pr_info("MS RAW ITO MIN MAX TEST:.................SKIPPED\n");
+ }
+ } else {
+ pr_info("MS RAW ITO TEST:.................SKIPPED\n");
+ }
+
+ERROR:
+ kfree(thresholds);
+ kfree(adj);
+ kfree(msRawFrame.node_data);
+ kfree(thresholds_min);
+ kfree(thresholds_max);
+ freeLimitsFile(&limit_file);
+ res |= fts_system_reset();
+ if (res < OK) {
+ pr_err("production_test_ito: ERROR %08X\n",
+ ERROR_PROD_TEST_ITO);
+ res = (res | ERROR_PROD_TEST_ITO);
+ }
+ return res;
+}
+
+/**
+ * Perform the Initialization of the IC
+ * @param type type of initialization to do
+ * (see @link sys_special_opt Initialization Options (Full or Panel) @endlink)
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_initialization(u8 type)
+{
+ int res;
+
+ pr_info("INITIALIZATION Production test is starting...\n");
+ if (type != SPECIAL_PANEL_INIT && type != SPECIAL_FULL_PANEL_INIT) {
+ pr_err("production_test_initialization: Type incompatible! Type = %02X ERROR %08X\n",
+ type, ERROR_OP_NOT_ALLOW |
+ ERROR_PROD_TEST_INITIALIZATION);
+ return ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_INITIALIZATION;
+ }
+
+ res = fts_system_reset();
+ if (res < 0) {
+ pr_err("production_test_initialization: ERROR %08X\n",
+ ERROR_PROD_TEST_INITIALIZATION);
+ return res | ERROR_PROD_TEST_INITIALIZATION;
+ }
+
+ pr_info("INITIALIZATION command sent... %02X\n", type);
+ res = writeSysCmd(SYS_CMD_SPECIAL, &type, 1);
+ if (res < OK) {
+ pr_err("production_test_initialization: ERROR %08X\n",
+ (res | ERROR_PROD_TEST_INITIALIZATION));
+ return res | ERROR_PROD_TEST_INITIALIZATION;
+ }
+
+
+ pr_info("Refresh Sys Info...\n");
+ res |= readSysInfo(1); /* need to update the chipInfo in order
+ * to refresh several versions */
+
+ if (res < 0) {
+ pr_err("production_test_initialization: read sys info ERROR %08X\n",
+ ERROR_PROD_TEST_INITIALIZATION);
+ res = (res | ERROR_PROD_TEST_INITIALIZATION);
+ }
+
+ return res;
+}
+
+
+// @param signature value of the MP flag to save if the Mass Production Test succeed
+/**
+ * Perform a FULL (ITO + INIT + DATA CHECK) Mass Production Test of the IC
+ * @param pathThresholds name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param saveInit if >0 (possible values: NO_INIT, SPECIAL_PANEL_INIT or
+ * SPECIAL_FULL_PANEL_INIT),
+ * the Initialization of the IC is executed otherwise it is skipped
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @param mpflag MP flag value to write in case of successful test
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_main(const char *pathThresholds, int stop_on_fail,
+ int saveInit, TestToDo *todo, u8 mpflag)
+{
+ int res, ret;
+
+ pr_info("MAIN Production test is starting...\n");
+
+#ifndef SKIP_PRODUCTION_TEST
+ pr_info("ITO TEST:\n");
+ res = production_test_ito(pathThresholds, todo, NULL);
+ if (res < 0) {
+ pr_err("Error during ITO TEST! ERROR %08X\n", res);
+ /* in case of ITO TEST failure is no sense keep going */
+ goto END;
+ } else
+ pr_info("ITO TEST OK!\n");
+#endif
+
+ pr_info("INITIALIZATION TEST :\n");
+ if (saveInit != NO_INIT) {
+ res = production_test_initialization((u8)saveInit);
+ if (res < 0) {
+ pr_err("Error during INITIALIZATION TEST! ERROR %08X\n",
+ res);
+ if (stop_on_fail)
+ goto END;
+ } else
+ pr_info("INITIALIZATION TEST OK!\n");
+ } else
+ pr_info("INITIALIZATION TEST :................. SKIPPED\n");
+
+ if (saveInit != NO_INIT) {
+ pr_info("Cleaning up...\n");
+ ret = fts_system_reset();
+ if (ret < 0) {
+ pr_err("production_test_main: system reset ERROR %08X\n",
+ ret);
+ res |= ret;
+ if (stop_on_fail)
+ goto END;
+ }
+ }
+
+#ifndef SKIP_PRODUCTION_TEST
+ pr_info("PRODUCTION DATA TEST:\n");
+ ret = production_test_data(pathThresholds, stop_on_fail, todo);
+ if (ret < OK)
+ pr_err("Error during PRODUCTION DATA TEST! ERROR %08X\n", ret);
+ else
+ pr_info("PRODUCTION DATA TEST OK!\n");
+#endif
+
+#ifdef COMPUTE_INIT_METHOD
+ if (saveInit != NO_INIT) {
+ pr_info("%s: Clearing the FIFO events!!!\n", __func__);
+ ret = flushFIFO();
+ if (ret < OK)
+ pr_err("%s: Error while Flushing the FIFO! ERROR %8X\n",
+ __func__, ret);
+ /* save the mp flag to desired value
+ * because data check OK
+ */
+ ret = saveMpFlag(mpflag);
+ if (ret < OK)
+ pr_err("Error while saving MP FLAG! ERROR %08X\n",
+ ret);
+ else
+ pr_info("MP FLAG saving OK!\n");
+ }
+#endif
+
+ res |= ret;
+ /* the OR is important because if the data test is OK but
+ * the init test fail, the main production test result should = FAIL */
+
+END:
+ if (res < OK) {
+ pr_err("MAIN Production test finished.................FAILED\n");
+ return res;
+ } else {
+ pr_info("MAIN Production test finished.................OK\n");
+ return OK;
+ }
+}
+
+/**
+ * Perform all the test selected in a TestTodo variable related to MS raw data
+ * (touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_raw(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret, count_fail = 0;
+ MutualSenseFrame msRawFrame;
+
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ u16 *adj = NULL;
+
+ int maxAdjH, maxAdjV;
+ int i, z;
+
+ msRawFrame.node_data = NULL;
+
+ /************** Mutual Sense Test *************/
+ pr_info("MS RAW DATA TEST is starting...\n");
+ if (todo->MutualRaw == 1 || todo->MutualRawGap == 1 ||
+ todo->MutualRawAdj == 1 || todo->MutualRawMap == 1 ||
+ todo->MutualRawAdjGap == 1 || todo->MutualRawAdjPeak == 1) {
+ ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+#ifdef READ_FILTERED_RAW
+ ret |= getMSFrame3(MS_FILTER, &msRawFrame);
+#else
+ ret |= getMSFrame3(MS_RAW, &msRawFrame);
+#endif
+ if (ret < OK) {
+ pr_err("production_test_data: getMSFrame failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ 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);
+
+ pr_info("MS RAW MIN MAX TEST:\n");
+ if (todo->MutualRaw == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_MIN_MAX,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax MS RAW failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW MIN MAX TEST:.................OK\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("MS RAW MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("MS RAW MAP MIN MAX TEST:\n");
+ if (todo->MutualRawMap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK || (trows !=
+ msRawFrame.header.force_node ||
+ tcolumns !=
+ msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK || (trows !=
+ msRawFrame.header.force_node ||
+ tcolumns !=
+ msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = checkLimitsMapTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMaxEachNodeData failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW MAP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else{
+ pr_info("MS RAW MAP MIN MAX TEST:.................OK\n");
+ }
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("MS RAW MAP MIN MAX TEST:.................SKIPPED\n");
+
+
+ pr_info("MS RAW GAP TEST:\n");
+ if (todo->MutualRawGap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap MS RAW failed... ERROR = %08X\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW GAP TEST:.................OK\n\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("MS RAW GAP TEST:.................SKIPPED\n");
+
+ pr_info("MS RAW ADJ TEST:\n");
+ if ((todo->MutualRawAdj == 1) || (todo->MutualRawAdjGap == 1) ||
+ (todo->MutualRawAdjPeak == 1)) {
+ pr_info("MS RAW ADJ HORIZONTAL TEST:\n");
+ ret = computeAdjHorizTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node,
+ &adj);
+ if (ret < OK) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ if (todo->MutualRawAdj) {
+ pr_err("MS RAW ADJ HORIZONTAL MIN/MAX:\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_ADJH,
+ &thresholds,
+ &trows,
+ &tcolumns);
+ if (ret < OK ||
+ (trows != msRawFrame.header.force_node ||
+ tcolumns !=
+ msRawFrame.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsMapAdjTotal(adj,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node - 1,
+ thresholds);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("%s MS RAW ADJ HORIZONTAL MIN/MAX:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW ADJ HORIZONTAL MIN/MAX:.................OK\n");
+
+ if (thresholds != NULL) {
+ kfree(thresholds);
+ thresholds = NULL;
+ }
+ }
+
+ if (todo->MutualRawAdjGap) {
+ pr_info("MS RAW ADJ HORIZONTAL GAP:\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_ADJH_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsGapOffsets(adj,
+ msRawFrame.header.
+ force_node,
+ msRawFrame.header.
+ sense_node - 1,
+ thresholds[0],
+ 1, 1, 1, 1);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJH GAP failed...\n");
+ pr_err("MS RAW ADJ HORIZONTAL GAP:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW ADJ HORIZONTAL GAP:.................OK\n");
+
+ if (thresholds != NULL) {
+ kfree(thresholds);
+ thresholds = NULL;
+ }
+ }
+
+
+ if (todo->MutualRawAdjPeak) {
+ int force_node = msRawFrame.header.force_node;
+ int sense_node = msRawFrame.header.sense_node;
+
+ pr_info("MS RAW ADJ Peak: Getting max ADJH\n");
+ maxAdjH = abs(adj[force_node + 1]);
+ /* skip nodes on the edges */
+ for (i = 1; i < force_node - 1; i++) {
+ for (z = 1; z < sense_node - 2; z++) {
+ maxAdjH =
+ max(maxAdjH,
+ abs(adj[(i * force_node) + z]));
+ }
+ }
+ }
+
+ kfree(adj);
+ adj = NULL;
+
+ pr_info("MS RAW ADJ VERTICAL TESTs:\n");
+ ret = computeAdjVertTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node,
+ &adj);
+ if (ret < OK) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ if (todo->MutualRawAdj) {
+ pr_info("MS RAW ADJ VERTICAL MIN/MAX:\n");
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_ADJV,
+ &thresholds,
+ &trows,
+ &tcolumns);
+ if (ret < OK ||
+ (trows !=
+ msRawFrame.header.force_node - 1 ||
+ tcolumns !=
+ msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsMapAdjTotal(adj,
+ msRawFrame.header.force_node - 1,
+ msRawFrame.header.sense_node,
+ thresholds);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJV failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW ADJ VERTICAL MIN/MAX:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW ADJ VERTICAL MIN/MAX:.................OK\n");
+
+ if (thresholds != NULL) {
+ kfree(thresholds);
+ thresholds = NULL;
+ }
+ }
+
+ if (todo->MutualRawAdjGap) {
+ pr_err("MS RAW ADJ VERTICAL GAP:\n");
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_ADJV_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJV_GAP failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsGapOffsets(adj,
+ msRawFrame.header.force_node - 1,
+ msRawFrame.header.sense_node,
+ thresholds[0], 1, 1, 1, 1);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJV GAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW ADJ VERTICAL GAP:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW ADJ VERTICAL GAP:.................OK\n");
+
+ if (thresholds != NULL) {
+ kfree(thresholds);
+ thresholds = NULL;
+ }
+ }
+
+ if (todo->MutualRawAdjPeak) {
+ int force_node = msRawFrame.header.force_node;
+ int sense_node = msRawFrame.header.sense_node;
+
+ pr_info("MS RAW ADJ Peak: Getting max ADJV\n");
+ maxAdjV = abs(adj[force_node + 1]);
+
+ /* skip nodes on the edges */
+ for (i = 1; i < (force_node - 2); i++) {
+ for (z = 1; z < sense_node - 1; z++) {
+ maxAdjV = (maxAdjV <
+ abs(adj[(i *
+ force_node) + z])) ?
+ abs(adj[(i *
+ force_node) + z]) :
+ maxAdjV;
+ }
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_ADJ_PEAK,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJV_PEAK failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ pr_info("maxAdjH = %d maxAdjV = %d threshold = %d\n",
+ maxAdjH, maxAdjV, thresholds[0]);
+
+ ret = OK;
+ if (maxAdjH > maxAdjV) {
+ if (maxAdjH > thresholds[0])
+ ret = ERROR_PROD_TEST_DATA;
+ } else {
+ if (maxAdjV > thresholds[0])
+ ret = ERROR_PROD_TEST_DATA;
+ }
+
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJV GAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW ADJ PEAK:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW ADJ PEAK:.................OK\n");
+
+ if (thresholds != NULL) {
+ kfree(thresholds);
+ thresholds = NULL;
+ }
+
+ }
+
+ kfree(adj);
+ adj = NULL;
+ } else
+ pr_info("MS RAW ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("MS RAW FRAME TEST:.................SKIPPED\n");
+
+ pr_info("MS KEY RAW TEST:\n");
+ if (todo->MutualKeyRaw == 1) {
+ ret = production_test_ms_key_raw(path_limits);
+ if (ret < 0) {
+ pr_err("production_test_data: production_test_ms_key_raw failed... ERROR = %08X\n",
+ ret);
+ count_fail += 1;
+ if (count_fail == 1) {
+ pr_err("MS RAW DATA TEST:.................FAIL fails_count = %d\n\n",
+ count_fail);
+ goto ERROR_LIMITS;
+ }
+ }
+ } else
+ pr_info("MS KEY RAW TEST:.................SKIPPED\n");
+
+ ret = production_test_ms_raw_lp(path_limits, stop_on_fail, todo);
+ if (ret < 0) {
+ pr_err("production_test_data: production_test_ms_raw_lp failed... ERROR = %08X\n",
+ ret);
+ count_fail += 1;
+ if (count_fail == 1) {
+ pr_err("MS RAW DATA TEST:.................FAIL fails_count = %d\n\n",
+ count_fail);
+ goto ERROR_LIMITS;
+ }
+ }
+
+ERROR:
+
+ if (count_fail == 0) {
+ if (msRawFrame.node_data != NULL) {
+ kfree(msRawFrame.node_data);
+ msRawFrame.node_data = NULL;
+ }
+ pr_info("MS RAW DATA TEST finished!.................OK\n");
+ return OK;
+ } else {
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (adj != NULL)
+ kfree(adj);
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ pr_err("MS RAW DATA TEST:.................FAIL fails_count = %d\n\n",
+ count_fail);
+ return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL;
+ }
+
+
+ERROR_LIMITS:
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ return ret;
+}
+
+
+/**
+ * Perform all the test selected in a TestTodo variable related to MS low power
+ * raw data
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_raw_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret, count_fail = 0;
+ MutualSenseFrame msRawFrame;
+
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ u16 *adj = NULL;
+
+ msRawFrame.node_data = NULL;
+
+ /************** Mutual Sense Test **************/
+ pr_info("MS RAW LP DATA TEST:\n");
+ if (todo->MutualRawLP == 1 || todo->MutualRawGapLP == 1 ||
+ todo->MutualRawAdjLP == 1 || todo->MutualRawMapLP) {
+ ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_ACTIVE);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+#ifdef READ_FILTERED_RAW
+ ret |= getMSFrame3(MS_FILTER, &msRawFrame);
+#else
+ ret |= getMSFrame3(MS_RAW, &msRawFrame);
+#endif
+ if (ret < 0) {
+ pr_err("production_test_data: getMSFrame failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ print_frame_short("MS Raw LP frame =",
+ array1dTo2d_short(
+ msRawFrame.node_data,
+ msRawFrame.node_data_size,
+ msRawFrame.header.sense_node),
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node);
+
+ pr_info("MS RAW LP MIN MAX TEST:\n");
+ if (todo->MutualRawLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_LP_MIN_MAX,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_LP_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax MS RAW LP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW LP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW LP MIN MAX TEST:.................OK\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("MS RAW LP MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("MS RAW LP MAP MIN MAX TEST:\n");
+ if (todo->MutualRawMapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_LP_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK ||
+ (trows != msRawFrame.header.force_node ||
+ tcolumns != msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_LP_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, MS_RAW_LP_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK ||
+ (trows != msRawFrame.header.force_node ||
+ tcolumns != msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_LP_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = checkLimitsMapTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMaxEachNodeData failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW LP MAP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else{
+ pr_info("MS RAW LP MAP MIN MAX TEST:.................OK\n");
+ }
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("MS RAW LP MAP MIN MAX TEST:.................SKIPPED\n");
+
+
+ pr_info("MS RAW LP GAP TEST:\n");
+ if (todo->MutualRawGapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_LP_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_LP_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap MS RAW LP failed... ERROR = %08X\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW LP GAP TEST:.................OK\n\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("MS RAW LP GAP TEST:.................SKIPPED\n");
+
+ pr_info("MS RAW LP ADJ TEST:\n");
+ if (todo->MutualRawAdjLP == 1) {
+ pr_info("MS RAW LP ADJ HORIZONTAL TEST:\n");
+ ret = computeAdjHorizTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node,
+ &adj);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_LP_ADJH,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msRawFrame.header.force_node ||
+ tcolumns !=
+ msRawFrame.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_LP_ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsMapAdjTotal(adj,
+ msRawFrame.header.
+ force_node,
+ msRawFrame.header.
+ sense_node - 1,
+ thresholds);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW LP ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW LP ADJ HORIZONTAL TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW LP ADJ HORIZONTAL TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ kfree(adj);
+ adj = NULL;
+
+ pr_info("MS RAW LP ADJ VERTICAL TEST:\n");
+ ret = computeAdjVertTotal(msRawFrame.node_data,
+ msRawFrame.header.force_node,
+ msRawFrame.header.sense_node,
+ &adj);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_RAW_LP_ADJV,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msRawFrame.header.force_node -
+ 1 || tcolumns !=
+ msRawFrame.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_RAW_ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+
+ ret = checkLimitsMapAdjTotal(adj,
+ msRawFrame.header.
+ force_node - 1,
+ msRawFrame.header.
+ sense_node, thresholds);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsAdj MS RAW ADJV failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS RAW LP ADJ VERTICAL TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail == 1)
+ goto ERROR;
+ } else
+ pr_info("MS RAW LP ADJ VERTICAL TEST:.................OK\n");
+ kfree(thresholds);
+ thresholds = NULL;
+
+ kfree(adj);
+ adj = NULL;
+ } else
+ pr_info("MS RAW LP ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("MS RAW LP FRAME TEST:.................SKIPPED\n");
+
+ERROR:
+ if (count_fail == 0) {
+ if (msRawFrame.node_data != NULL) {
+ kfree(msRawFrame.node_data);
+ msRawFrame.node_data = NULL;
+ }
+ pr_info("MS RAW DATA TEST finished!.................OK\n");
+ return OK;
+ } else {
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (adj != NULL)
+ kfree(adj);
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ pr_err("MS RAW LP DATA TEST:.................FAIL fails_count = %d\n\n",
+ count_fail);
+ return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL;
+ }
+
+
+ERROR_LIMITS:
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ return ret;
+}
+
+/**
+ * Perform MS raw test for keys data
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_key_raw(const char *path_limits)
+{
+ int ret;
+ MutualSenseFrame msRawFrame;
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+
+ /************** Mutual Sense Test **************/
+ pr_info("MS KEY RAW DATA TEST is starting...\n");
+ ret = setScanMode(SCAN_MODE_ACTIVE, 0xFF);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+ ret |= getMSFrame3(MS_KEY_RAW, &msRawFrame);
+ if (ret < 0) {
+ pr_err("production_test_data: getMSKeyFrame failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_RAW_MIN_MAX, &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_RAW_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax MS KEY RAW failed... ERROR COUNT = %d\n",
+ ret);
+ goto ERROR;
+ } else
+ pr_info("MS KEY RAW TEST:.................OK\n\n");
+
+ 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);
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ pr_err("MS KEY RAW TEST:.................FAIL\n\n");
+ return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL;
+
+ERROR_LIMITS:
+ if (msRawFrame.node_data != NULL)
+ kfree(msRawFrame.node_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ return ret;
+}
+
+/**
+ * Perform all the tests selected in a TestTodo variable related to MS Init
+ * data (touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_cx(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+
+ int *thresholds = NULL;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ int trows, tcolumns;
+
+ MutualSenseData msCompData;
+ TotMutualSenseData totCompData;
+
+ u8 *adjhor = NULL;
+
+ u8 *adjvert = NULL;
+
+ u16 container;
+ /* u16 *total_cx = NULL; */
+ u16 *total_adjhor = NULL;
+ u16 *total_adjvert = NULL;
+
+
+ /* MS CX TEST */
+ pr_info("MS CX Testes are starting...\n");
+
+ ret = readMutualSenseCompensationData(LOAD_CX_MS_TOUCH, &msCompData);
+ /* read MS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ ret = readTotMutualSenseCompensationData(LOAD_PANEL_CX_TOT_MS_TOUCH,
+ &totCompData);
+ /* read TOT MS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readTotMutualSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ kfree(msCompData.node_data);
+ msCompData.node_data = NULL;
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ pr_info("MS CX1 TEST:\n");
+ if (todo->MutualCx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX1_MIN_MAX, &thresholds,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX1_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS CX1 TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS CX1 TEST:.................OK\n\n");
+ } else
+ pr_info("MS CX1 TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ pr_info("MS CX2 MIN MAX TEST:\n");
+ if (todo->MutualCx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_MAP_MIN, &thresholds_min,
+ &trows, &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_MAP_MAX, &thresholds_max,
+ &trows, &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_MAP_MAX failed... ERROR %08X\n",
+ 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) {
+ pr_err("production_test_data: checkLimitsMap MS CX2 MIN MAX failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS CX2 MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS CX2 MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("MS CX2 MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("MS CX2 ADJ TEST:\n");
+ if (todo->MutualCx2Adj == 1) {
+ /* MS CX2 ADJ HORIZ */
+ pr_info("MS CX2 ADJ HORIZ TEST:\n");
+
+ ret = computeAdjHoriz(msCompData.node_data,
+ msCompData.header.force_node,
+ msCompData.header.sense_node,
+ &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS CX2 ADJ HORIZ computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_ADJH_MAP_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMapAdj CX2 ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS CX2 ADJ HORIZ TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS CX2 ADJ HORIZ TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+
+ /* MS CX2 ADJ VERT */
+ pr_info("MS CX2 ADJ VERT TEST:\n");
+
+ ret = computeAdjVert(msCompData.node_data,
+ msCompData.header.force_node,
+ msCompData.header.sense_node,
+ &adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS CX2 ADJ VERT computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msCompData.header.force_node - 1 ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_ADJV_MAP_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMapAdj CX2 ADJV failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS CX2 ADJ HORIZ TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS CX2 ADJ VERT TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("MS CX2 ADJ TEST:.................SKIPPED\n\n");
+
+ /* START OF TOTAL CHECK */
+ pr_info("MS TOTAL CX TEST:\n");
+
+ if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) {
+ pr_info("MS TOTAL CX MIN MAX TEST:\n");
+ if (todo->MutualCxTotal == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap MS TOTAL CX TEST failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("MS TOTAL CX MIN MAX TEST:.................SKIPPED\n\n");
+
+
+ pr_info("MS TOTAL CX ADJ TEST:\n");
+ if (todo->MutualCxTotalAdj == 1) {
+ /* MS TOTAL CX ADJ HORIZ */
+ pr_info("MS TOTAL CX ADJ HORIZ TEST:\n");
+
+ ret = computeAdjHorizTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS TOTAL CX ADJ HORIZ computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_ADJH_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor,
+ totCompData.header.
+ force_node,
+ totCompData.header.
+ sense_node - 1,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX ADJ HORIZ TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+
+ /* MS TOTAL CX ADJ VERT */
+ pr_info("MS TOTAL CX ADJ VERT TEST:\n");
+
+ ret = computeAdjVertTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ &total_adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS TOTAL CX ADJ VERT computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_ADJV_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.
+ force_node - 1,
+ totCompData.header.
+ sense_node - 1,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX ADJ VERT TEST:.................OK\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("MS TOTAL CX ADJ TEST:.................SKIPPED\n");
+
+ kfree(totCompData.node_data);
+ totCompData.node_data = NULL;
+ } else
+ pr_info("MS TOTAL CX TEST:.................SKIPPED\n");
+
+ if ((todo->MutualCx1LP | todo->MutualCx2LP | todo->MutualCx2AdjLP |
+ todo->MutualCxTotalLP | todo->MutualCxTotalAdjLP) == 1) {
+ ret = production_test_ms_cx_lp(path_limits, stop_on_fail, todo);
+ if (ret < OK) {
+ count_fail += 1;
+ pr_err("production_test_data: production_test_cx_lp failed... ERROR = %08X\n",
+ ret);
+ pr_err("MS CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ return ret;
+ }
+ } else
+ pr_info("%s MS CX LP TEST:.................SKIPPED\n");
+
+ 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;
+ pr_err("production_test_data: production_test_ms_key_cx failed... ERROR = %08X\n",
+ ret);
+ pr_err("MS CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ return ret;
+ }
+ } else
+ pr_info("MS KEY CX TEST:.................SKIPPED\n");
+
+ERROR:
+
+ if (count_fail == 0) {
+ pr_info("MS CX testes finished!.................OK\n");
+ kfree(msCompData.node_data);
+ msCompData.node_data = NULL;
+ return OK;
+ } else {
+ print_frame_i8("MS Init Data (Cx2) =", array1dTo2d_i8(
+ msCompData.node_data,
+ msCompData.node_data_size,
+ msCompData.header.sense_node),
+ msCompData.header.force_node,
+ msCompData.header.sense_node);
+ print_frame_short(" TOT MS Init Data (Cx) =", array1dTo2d_short(
+ totCompData.node_data,
+ totCompData.node_data_size,
+ totCompData.header.sense_node),
+ totCompData.header.force_node,
+ totCompData.header.sense_node);
+ pr_err("MS CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+ }
+
+ERROR_LIMITS:
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ return ret;
+}
+
+/**
+ * Perform all the tests selected in a TestTodo variable related to MS Init
+ * data of the keys
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_key_cx(const char *path_limits, int stop_on_fail,
+ 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;
+
+ MutualSenseData msCompData;
+ TotMutualSenseData totCompData;
+
+
+ short container;
+
+
+ /* MS CX TEST */
+ pr_info("MS KEY CX Testes are starting...\n");
+
+ ret = readMutualSenseCompensationData(LOAD_CX_MS_KEY, &msCompData);
+ /* read MS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ if (msCompData.header.force_node > msCompData.header.sense_node)
+ /* the meaningful data are only in the first row,
+ * the other rows are only a copy of the first one */
+ num_keys = msCompData.header.force_node;
+ else
+ num_keys = msCompData.header.sense_node;
+
+ pr_info("MS KEY CX1 TEST:\n");
+ if (todo->MutualKeyCx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_CX1_MIN_MAX, &thresholds,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_CX1_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)msCompData.cx1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS KEY CX1 TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS KEY CX1 TEST:.................OK\n\n");
+ } else
+ pr_info("MS KEY CX1 TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ pr_info("MS KEY CX2 TEST:\n");
+ if (todo->MutualKeyCx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_CX2_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_CX2_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MAX failed... ERROR %08X\n",
+ 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) {
+ pr_err("production_test_data: checkLimitsMap MS KEY CX2 failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS KEY CX2 TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS KEY CX2 TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("MS CX2 TEST:.................SKIPPED\n\n");
+
+ /* START OF TOTAL CHECK */
+ pr_info("MS KEY TOTAL CX TEST:\n");
+
+ if (todo->MutualKeyCxTotal == 1) {
+ ret = readTotMutualSenseCompensationData(
+ LOAD_PANEL_CX_TOT_MS_KEY, &totCompData);
+ if (ret < 0) {
+ pr_err("production_test_data: computeTotalCx failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_TOTAL_CX_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node ||
+ tcolumns != totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_KEY_TOTAL_CX_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node ||
+ tcolumns != totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ thresholds_min, thresholds_max);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap MS TOTAL KEY CX TEST failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS KEY TOTAL CX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS KEY TOTAL CX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+
+ kfree(totCompData.node_data);
+ totCompData.node_data = NULL;
+ } else
+ pr_info("MS KEY TOTAL CX TEST:.................SKIPPED\n");
+
+
+ERROR:
+ if (count_fail == 0) {
+ pr_info("MS KEY CX testes finished!.................OK\n");
+ kfree(msCompData.node_data);
+ msCompData.node_data = NULL;
+ return OK;
+ } else {
+ print_frame_i8("MS Key Init Data (Cx2) =", array1dTo2d_i8(
+ msCompData.node_data,
+ msCompData.node_data_size,
+ msCompData.header.sense_node),
+ msCompData.header.force_node,
+ msCompData.header.sense_node);
+ pr_err("MS Key CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+ }
+
+ERROR_LIMITS:
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ return ret;
+}
+
+/**
+ * Perform all the tests selected in a TestTodo variable related to MS LowPower
+ * Init data (touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ms_cx_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+
+ int *thresholds = NULL;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ int trows, tcolumns;
+
+ MutualSenseData msCompData;
+ TotMutualSenseData totCompData;
+
+ u8 *adjhor = NULL;
+
+ u8 *adjvert = NULL;
+
+ u16 container;
+ /* u16 *total_cx = NULL; */
+ u16 *total_adjhor = NULL;
+ u16 *total_adjvert = NULL;
+
+
+ /* MS CX TEST */
+ pr_info("MS LP CX Testes are starting...\n");
+
+ ret = readMutualSenseCompensationData(LOAD_CX_MS_LOW_POWER,
+ &msCompData);
+ /* read MS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ ret = readTotMutualSenseCompensationData(LOAD_PANEL_CX_TOT_MS_LOW_POWER,
+ &totCompData);
+ /* read TOT MS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readTotMutualSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ kfree(msCompData.node_data);
+ msCompData.node_data = NULL;
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ pr_info("MS LP CX1 TEST:\n");
+ if (todo->MutualCx1LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX1_LP_MIN_MAX, &thresholds,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX1_LP_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax MS LP CX1 failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS LP CX1 TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS LP CX1 TEST:.................OK\n\n");
+ } else
+ pr_info("MS LP CX1 TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+
+ pr_info("%s MS LP CX2 MIN MAX TEST:\n");
+ if (todo->MutualCx2LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_LP_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_LP_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_LP_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_LP_MAP_MAX failed... ERROR %08X\n",
+ 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) {
+ pr_err("production_test_data: checkLimitsMap MS LP CX2 MIN MAX failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS LP CX2 MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS LP CX2 MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_err("MS LP CX2 MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("MS LP CX2 ADJ TEST:\n");
+ if (todo->MutualCx2AdjLP == 1) {
+ /* MS CX2 ADJ HORIZ */
+ pr_info("MS LP CX2 ADJ HORIZ TEST:\n");
+
+ ret = computeAdjHoriz(msCompData.node_data,
+ msCompData.header.force_node,
+ msCompData.header.sense_node,
+ &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS LP CX2 ADJ HORIZ computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_ADJH_LP_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msCompData.header.force_node ||
+ tcolumns != msCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_ADJH_LP_MAP_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMapAdj CX2 ADJH LP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS LP CX2 ADJ HORIZ TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS LP CX2 ADJ HORIZ TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+
+ /* MS CX2 ADJ VERT */
+ pr_info("MS LP CX2 ADJ VERT TEST:\n");
+
+ ret = computeAdjVert(msCompData.node_data,
+ msCompData.header.force_node,
+ msCompData.header.sense_node,
+ &adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS LP CX2 ADJ VERT computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ MS_CX2_ADJV_LP_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != msCompData.header.force_node - 1 ||
+ tcolumns != msCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_CX2_ADJV_LP_MAP_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMapAdj CX2 ADJV LP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS LP CX2 ADJ HORIZ TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS LP CX2 ADJ VERT TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("MS LP CX2 ADJ TEST:.................SKIPPED\n\n");
+
+ /* START OF TOTAL CHECK */
+ pr_info("MS TOTAL LP CX TEST:\n");
+
+ if (todo->MutualCxTotalLP == 1 || todo->MutualCxTotalAdjLP == 1) {
+ pr_info("MS TOTAL LP CX MIN MAX TEST:\n");
+ if (todo->MutualCxTotalLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_LP_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_LP_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_LP_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_LP_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap MS TOTAL CX LP TEST failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX LP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX LP MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("MS TOTAL CX LP MIN MAX TEST:.................SKIPPED\n\n");
+
+
+ pr_info("MS TOTAL CX ADJ LP TEST:\n");
+ if (todo->MutualCxTotalAdjLP == 1) {
+ /* MS TOTAL CX ADJ HORIZ */
+ pr_info("MS TOTAL CX ADJ HORIZ LP TEST:\n");
+
+ ret = computeAdjHorizTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS TOTAL CX ADJ HORIZ LP computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_ADJH_LP_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_LP_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor,
+ totCompData.header.force_node,
+ totCompData.header.sense_node - 1,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH LP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX ADJ HORIZ LP TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX ADJ HORIZ LP TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+
+ /* MS TOTAL CX ADJ VERT */
+ pr_info("MS TOTAL CX ADJ VERT LP TEST:\n");
+
+ ret = computeAdjVertTotal(totCompData.node_data,
+ totCompData.header.force_node,
+ totCompData.header.sense_node,
+ &total_adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("MS TOTAL CX ADJ VERT LP computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ MS_TOTAL_CX_ADJV_LP_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_LP_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.force_node - 1,
+ totCompData.header.sense_node - 1,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("MS TOTAL CX ADJ HORIZ LP TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("MS TOTAL CX ADJ VERT LP TEST:.................OK\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("MS TOTAL CX ADJ LP TEST:.................SKIPPED\n");
+
+ kfree(totCompData.node_data);
+ totCompData.node_data = NULL;
+ } else
+ pr_info("MS TOTAL CX LP TEST:.................SKIPPED\n");
+
+
+
+ERROR:
+
+ if (count_fail == 0) {
+ pr_info("MS LP CX testes finished!.................OK\n");
+ kfree(msCompData.node_data);
+ msCompData.node_data = NULL;
+ return OK;
+ }
+
+ print_frame_i8("MS LP Init Data (Cx2) =", array1dTo2d_i8(
+ msCompData.node_data,
+ msCompData.node_data_size,
+ msCompData.header.sense_node),
+ msCompData.header.force_node,
+ msCompData.header.sense_node);
+ print_frame_short(" TOT MS LP Init Data (Cx) =",
+ array1dTo2d_short(
+ totCompData.node_data,
+ totCompData.node_data_size,
+ totCompData.header.sense_node),
+ totCompData.header.force_node,
+ totCompData.header.sense_node);
+ pr_err("MS LP CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+
+ERROR_LIMITS:
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (totCompData.node_data != NULL)
+ kfree(totCompData.node_data);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (msCompData.node_data != NULL)
+ kfree(msCompData.node_data);
+ return ret;
+}
+
+/**
+ * Perform all the test selected in a TestTodo variable related to SS raw data
+ *(touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ss_raw(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+ int rows, columns;
+
+ SelfSenseFrame ssRawFrame;
+
+ int *thresholds = NULL;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+ int trows, tcolumns;
+
+ /* SS TEST */
+ pr_info("SS RAW Testes are starting...\n");
+
+ /************** Self Sense Test **************/
+
+ pr_info("Getting SS Frame...\n");
+ ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+#ifdef READ_FILTERED_RAW
+ ret |= getSSFrame3(SS_FILTER, &ssRawFrame);
+#else
+ ret |= getSSFrame3(SS_RAW, &ssRawFrame);
+#endif
+ if (ret < 0) {
+ pr_err("production_test_data: getSSFrame failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ print_frame_short("SS Raw force frame =",
+ array1dTo2d_short(
+ ssRawFrame.force_data,
+ ssRawFrame.header.force_node,
+ 1),
+ ssRawFrame.header.force_node, 1);
+ print_frame_short("SS Raw sense frame =",
+ array1dTo2d_short(
+ ssRawFrame.sense_data,
+ ssRawFrame.header.sense_node,
+ ssRawFrame.header.sense_node),
+ 1, ssRawFrame.header.sense_node);
+
+ /* SS RAW (PROXIMITY) FORCE TEST */
+ pr_info("SS RAW FORCE TEST:\n");
+
+
+
+ if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1
+ || todo->SelfForceRawMap == 1) {
+ columns = 1; /* there are no data for the sense channels
+ * because is a force frame
+ */
+ rows = ssRawFrame.header.force_node;
+
+ pr_info("SS RAW FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceRaw == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_FORCE_MIN_MAX,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %08X\n",
+ 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) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW FORCE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW FORCE MAP MIN MAX TEST:\n");
+ if (todo->SelfForceRawMap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_FORCE_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_FORCE_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(ssRawFrame.force_data, rows,
+ columns, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW FORCE MAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW FORCE MAP 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
+ pr_info("SS RAW FORCE MAP MIN MAX TEST:.................OK\n\n");
+
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("SS RAW FORCE MAP MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW FORCE GAP TEST:\n");
+ if (todo->SelfForceRawGap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_FORCE_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap SS RAW FORCE GAP failed... ERROR = %08X\n",
+ ret);
+ pr_err("SS RAW FORCE GAP TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW FORCE GAP TEST:.................OK\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW FORCE GAP TEST:.................SKIPPED\n\n");
+
+ kfree(ssRawFrame.force_data);
+ ssRawFrame.force_data = NULL;
+ } else
+ pr_info("SS RAW FORCE TEST:.................SKIPPED\n\n");
+
+ /* SS RAW (PROXIMITY) SENSE TEST */
+ pr_info("SS RAW SENSE TEST:\n");
+
+ if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1 ||
+ todo->SelfSenseRawMap == 1) {
+ columns = ssRawFrame.header.sense_node;
+ rows = 1; /* there are no data for the force channels
+ * because is a sense frame
+ */
+
+ pr_info("SS RAW SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseRaw == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_SENSE_MIN_MAX,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_SENSE_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW SENSE MIN MAX TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW SENSE MIN MAX TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW SENSE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS RAW SENSE MAP MIN MAX TEST:\n");
+ if (todo->SelfSenseRawMap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_SENSE_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_SENSE_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_SENSE_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_SENSE_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(ssRawFrame.sense_data, rows,
+ columns, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW SENSE MAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW SENSE MAP MIN MAX TEST:.................FAIL\n\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
+ pr_info("SS RAW SENSE MAP MIN MAX TEST:.................OK\n\n");
+
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("SS RAW SENSE MAP MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW SENSE GAP TEST:\n");
+ if (todo->SelfSenseRawGap == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_SENSE_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_SENSE_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap SS RAW SENSE GAP failed... ERROR = %08X\n",
+ ret);
+ pr_err("SS RAW SENSE GAP TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW SENSE GAP TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW SENSE GAP TEST:.................SKIPPED\n");
+
+ kfree(ssRawFrame.sense_data);
+ ssRawFrame.sense_data = NULL;
+ } else
+ pr_info("SS RAW SENSE TEST:.................SKIPPED\n\n");
+
+ ret = production_test_ss_raw_lp(path_limits, stop_on_fail, todo);
+ if (ret < OK) {
+ pr_err("production_test_data: ss_raw_lp failed... ERROR = %08X\n",
+ ret);
+ count_fail += 1;
+ }
+
+ if (count_fail == 0) {
+ pr_info("SS RAW testes finished!.................OK\n\n");
+ return OK;
+ }
+
+ pr_err("SS RAW testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+
+
+ERROR_LIMITS:
+ if (ssRawFrame.force_data != NULL)
+ kfree(ssRawFrame.force_data);
+ if (ssRawFrame.sense_data != NULL)
+ kfree(ssRawFrame.sense_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+
+ return ret;
+}
+
+
+/*
+ * Perform all the test selected in a TestTodo variable related to SS raw data
+ * low power
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ss_raw_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+ int rows, columns;
+
+ SelfSenseFrame ssRawFrame;
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+
+ /* SS TEST */
+ pr_info("SS RAW LP Testes are starting...\n");
+
+ /************** Self Sense Test **************/
+
+ pr_info("Getting SS LP Frame...\n");
+ ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_DETECT);
+ msleep(WAIT_FOR_FRESH_FRAMES);
+ ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ msleep(WAIT_AFTER_SENSEOFF);
+#ifdef READ_FILTERED_RAW
+ ret |= getSSFrame3(SS_DETECT_FILTER, &ssRawFrame);
+#else
+ ret |= getSSFrame3(SS_DETECT_RAW, &ssRawFrame);
+#endif
+ if (ret < 0) {
+ pr_err("production_test_data: getSSFrame failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ print_frame_short("SS Raw LP force frame =",
+ array1dTo2d_short(
+ ssRawFrame.force_data,
+ ssRawFrame.header.force_node, 1),
+ ssRawFrame.header.force_node, 1);
+ print_frame_short("SS Raw LP sense frame =",
+ array1dTo2d_short(
+ ssRawFrame.sense_data,
+ ssRawFrame.header.sense_node,
+ ssRawFrame.header.sense_node),
+ 1, ssRawFrame.header.sense_node);
+
+ /* SS RAW (PROXIMITY) FORCE TEST */
+ pr_info("SS RAW LP FORCE TEST:\n");
+
+ if ((todo->SelfForceRawLP == 1 || todo->SelfForceRawGapLP == 1 ||
+ todo->SelfForceRawMapLP == 1) &&
+ ssRawFrame.header.force_node != 0) {
+ columns = 1; /* there are no data for the sense channels
+ * because is a force frame
+ */
+ rows = ssRawFrame.header.force_node;
+
+ pr_info("SS RAW LP FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceRawLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_LP_FORCE_MIN_MAX,
+ &thresholds,
+ &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %08X\n",
+ 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) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW LP FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW LP FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW LP FORCE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW LP FORCE MAP MIN MAX TEST:\n");
+ if (todo->SelfForceRawMapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_LP_FORCE_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_FORCE_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_LP_FORCE_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_FORCE_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(ssRawFrame.force_data, rows,
+ columns, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW LP FORCE MAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW LP FORCE MAP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ print_frame_short("SS Raw LP 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
+ pr_info("SS RAW FORCE LP MAP MIN MAX TEST:.................OK\n\n");
+
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("SS RAW FORCE LP MAP MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW LP FORCE GAP TEST:\n");
+ if (todo->SelfForceRawGapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_LP_FORCE_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap SS RAW FORCE GAP failed... ERROR = %08X\n",
+ ret);
+ pr_err("SS RAW LP FORCE GAP TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW LP FORCE GAP TEST:.................OK\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW LP FORCE GAP TEST:.................SKIPPED\n\n");
+
+ kfree(ssRawFrame.force_data);
+ ssRawFrame.force_data = NULL;
+ } else
+ pr_info("SS RAW LP FORCE TEST:.................SKIPPED\n\n");
+
+ /* SS RAW (PROXIMITY) SENSE TEST */
+ pr_info("SS RAW LP SENSE TEST:\n");
+
+ if ((todo->SelfSenseRawLP == 1 || todo->SelfSenseRawGapLP == 1 ||
+ todo->SelfSenseRawMapLP == 1) &&
+ ssRawFrame.header.sense_node != 0){
+ columns = ssRawFrame.header.sense_node;
+ rows = 1; /* there are no data for the force channels
+ * because is a sense frame
+ */
+
+ pr_info("SS RAW LP SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseRawLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_LP_SENSE_MIN_MAX,
+ &thresholds,
+ &trows, &tcolumns);
+ if (ret < OK || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_MIN_MAX failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW LP SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW LP SENSE MIN MAX TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW SENSE MIN MAX TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW LP SENSE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS RAW LP SENSE MAP MIN MAX TEST:\n");
+ if (todo->SelfSenseRawMapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_LP_SENSE_EACH_NODE_MIN,
+ &thresholds_min, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_EACH_NODE_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file, SS_RAW_LP_SENSE_EACH_NODE_MAX,
+ &thresholds_max, &trows, &tcolumns);
+ if (ret < OK || (trows != rows ||
+ tcolumns != columns)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_EACH_NODE_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(ssRawFrame.sense_data, rows,
+ columns, thresholds_min,
+ thresholds_max);
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS RAW LP SENSE MAP failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS RAW LP SENSE MAP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ print_frame_short("SS Raw LP 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
+ pr_info("SS RAW LP SENSE MAP MIN MAX TEST:.................OK\n\n");
+
+ if (thresholds_min != NULL) {
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ }
+ if (thresholds_max != NULL) {
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ }
+ } else
+ pr_info("SS RAW LP SENSE MAP MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS RAW LP SENSE GAP TEST:\n");
+ if (todo->SelfSenseRawGapLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_RAW_LP_SENSE_GAP,
+ &thresholds, &trows,
+ &tcolumns);
+ if (ret < OK || (trows != 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_GAP failed... ERROR %08X\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) {
+ pr_err("production_test_data: checkLimitsGap SS RAW LP SENSE GAP failed... ERROR = %08X\n",
+ ret);
+ pr_err("SS RAW LP SENSE GAP TEST:.................FAIL\n");
+ count_fail += 1;
+ if (stop_on_fail) {
+ ret = ERROR_PROD_TEST_DATA |
+ ERROR_TEST_CHECK_FAIL;
+ goto ERROR_LIMITS;
+ }
+ } else
+ pr_info("SS RAW LP SENSE GAP TEST:.................OK\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS RAW LP SENSE GAP TEST:.................SKIPPED\n");
+
+ kfree(ssRawFrame.sense_data);
+ ssRawFrame.sense_data = NULL;
+ } else
+ pr_info("SS RAW LP SENSE TEST:.................SKIPPED\n\n");
+
+ if (count_fail == 0) {
+ pr_info("SS RAW LP testes finished!.................OK\n\n");
+ return OK;
+ }
+
+ pr_err("SS RAW LP testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+
+ERROR_LIMITS:
+ if (ssRawFrame.force_data != NULL)
+ kfree(ssRawFrame.force_data);
+ if (ssRawFrame.sense_data != NULL)
+ kfree(ssRawFrame.sense_data);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+
+ return ret;
+}
+
+/*
+ * Perform all the tests selected in a TestTodo variable related to SS Init
+ * data (touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ss_ix_cx(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+
+ SelfSenseData ssCompData;
+ TotSelfSenseData totCompData;
+
+ u8 *adjhor = NULL;
+ u8 *adjvert = NULL;
+
+ short container;
+
+ u16 *total_adjhor = NULL;
+ u16 *total_adjvert = NULL;
+
+ pr_info("SS IX CX testes are starting...\n");
+ ret = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH, &ssCompData);
+ /* read the SS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readSelfSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ ret = readTotSelfSenseCompensationData(LOAD_PANEL_CX_TOT_SS_TOUCH,
+ &totCompData);
+ /* read the TOT SS compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readTotSelfSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ kfree(ssCompData.ix2_fm);
+ kfree(ssCompData.ix2_sn);
+ kfree(ssCompData.cx2_fm);
+ kfree(ssCompData.cx2_sn);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ /************* SS FORCE IX **************/
+ /* SS IX1 FORCE TEST */
+ pr_info("SS IX1 FORCE TEST:\n");
+ if (todo->SelfForceIx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX1_FORCE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX1_FORCE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ container = (short)ssCompData.f_ix1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS IX1 FORCE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX1 FORCE TEST:.................OK\n\n");
+ } else
+ pr_info("SS IX1 FORCE TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ /* SS IX2 FORCE TEST */
+ pr_info("SS IX2 FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceIx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_FORCE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_FORCE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapFromU(ssCompData.ix2_fm,
+ ssCompData.header.force_node, 1,
+ thresholds_min,
+ thresholds_max); /* check the
+ * values with
+ * thresholds
+ */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS IX2 FORCE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS IX2 FORCE ADJ TEST:\n");
+ if (todo->SelfForceIx2Adj == 1) {
+ /* SS IX2 FORCE ADJV TEST */
+ pr_info("SS IX2 FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertFromU(ssCompData.ix2_fm,
+ ssCompData.header.force_node, 1,
+ &adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS IX2 FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS IX2 FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns); /* load the max
+ * thresholds
+ */
+ if (ret < 0 || (trows != ssCompData.header.force_node - 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node -
+ 1, 1, thresholds_max); /* check the
+ * values with
+ * thresholds
+ */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("SS IX2 FORCE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL FORCE IX */
+ pr_info("SS TOTAL IX FORCE TEST:\n");
+ if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) {
+ pr_info("SS TOTAL IX FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceIxTotal == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_FORCE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_FORCE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotalFromU(totCompData.ix_fm,
+ totCompData.header.force_node,
+ 1,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL IX FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS TOTAL IX FORCE ADJ TEST:\n");
+ if (todo->SelfForceIxTotalAdj == 1) {
+ /* SS TOTAL IX FORCE ADJV TEST */
+ pr_info("SS TOTAL IX FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertTotalFromU(totCompData.ix_fm,
+ totCompData.header.force_node, 1,
+ &total_adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS TOTAL IX FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL IX FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_ADJV_MAP_MAX... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.force_node - 1, 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("SS TOTAL IX FORCE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL IX FORCE TEST:.................SKIPPED\n\n");
+
+
+ /************** SS SENSE IX **************/
+ /* SS IX1 SENSE TEST */
+ pr_info("SS IX1 SENSE TEST:\n");
+ if (todo->SelfSenseIx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX1_SENSE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX1_SENSE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.s_ix1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS IX1 SENSE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX1 SENSE TEST:.................OK\n\n");
+ } else
+ pr_info("SS IX1 SENSE TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ /* SS IX2 SENSE TEST */
+ pr_info("SS IX2 SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseIx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_SENSE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_SENSE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapFromU(ssCompData.ix2_sn, 1,
+ ssCompData.header.sense_node,
+ thresholds_min, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS IX2 SENSE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS IX2 SENSE ADJ TEST:\n");
+ if (todo->SelfSenseIx2Adj == 1) {
+ /* SS IX2 SENSE ADJH TEST */
+ pr_info("SS IX2 SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizFromU(ssCompData.ix2_sn, 1,
+ ssCompData.header.sense_node,
+ &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS IX2 SENSE ADJ HORIZ computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ 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)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjhor, 1,
+ ssCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS IX2 SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+ } else
+ pr_info("SS IX2 SENSE ADJ TEST:.................SKIPPED\n");
+
+ /* SS TOTAL IX SENSE */
+ pr_info("SS TOTAL IX SENSE TEST:\n");
+ if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) {
+ pr_info("SS TOTAL IX SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseIxTotal == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_SENSE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_SENSE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotalFromU(totCompData.ix_sn, 1,
+ totCompData.header.sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL IX SENSE MIN MAX TEST:.................SKIPPED\n");
+
+
+ pr_info("SS TOTAL IX SENSE ADJ TEST:\n");
+ if (todo->SelfSenseIxTotalAdj == 1) {
+ /* SS TOTAL IX SENSE ADJH TEST */
+ pr_info("SS TOTAL IX SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizTotalFromU(totCompData.ix_sn, 1,
+ totCompData.header.sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS TOTAL IX SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL IX SENSE ADJ HORIZ computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor, 1,
+ totCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS TOTAL IX SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+ } else
+ pr_info("SS TOTAL IX SENSE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL IX SENSE TEST:.................SKIPPED\n");
+
+ /************* SS SENSE CX **************/
+ /* SS CX1 FORCE TEST */
+ pr_info("SS CX1 FORCE TEST:\n");
+ if (todo->SelfForceCx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX1_FORCE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX1_FORCE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.f_cx1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS CX1 FORCE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX1 FORCE TEST:.................OK\n\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS CX1 FORCE TEST:.................SKIPPED\n\n");
+
+ /* SS CX2 FORCE TEST */
+ pr_info("SS CX2 FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceCx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_FORCE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_FORCE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMap(ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS CX2 FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS CX2 FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS CX2 FORCE ADJ TEST:\n");
+ if (todo->SelfForceCx2Adj == 1) {
+ /* SS CX2 FORCE ADJV TEST */
+ pr_info("SS CX2 FORCE ADJVERT TEST:\n");
+ ret = computeAdjVert(ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1, &adjvert);
+ /* compute the ADJV for CX2 FORCE */
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS CX2 FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS CX2 FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node - 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node -
+ 1, 1, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("SS CX2 FORCE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL CX FORCE */
+ pr_info("SS TOTAL CX FORCE TEST:\n");
+ if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) {
+ pr_info("SS TOTAL CX FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceCxTotal == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_FORCE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_FORCE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.cx_fm,
+ totCompData.header.force_node,
+ 1, thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL CX FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ /* SS TOTAL CX FORCE ADJV TEST */
+ pr_info("SS TOTAL CX FORCE ADJ TEST:\n");
+ if (todo->SelfForceCxTotalAdj == 1) {
+ pr_info("SS TOTAL CX FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertTotal(totCompData.cx_fm,
+ totCompData.header.force_node,
+ 1, &total_adjvert);
+ /* compute the ADJV for CX2 FORCE */
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS TOTAL CX FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL CX FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.force_node - 1, 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL CX FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("SS TOTAL CX FORCE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL CX FORCE TEST:.................SKIPPED\n\n");
+
+
+
+ /************* SS SENSE CX *************/
+ /* SS CX1 SENSE TEST */
+ pr_info("SS CX1 SENSE TEST:\n");
+ if (todo->SelfSenseCx1 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX1_SENSE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX1_SENSE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.s_cx1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS CX1 SENSE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX1 SENSE TEST:.................OK\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS CX1 SENSE TEST:.................SKIPPED\n\n");
+
+
+ /* SS CX2 SENSE TEST */
+ pr_info("SS CX2 SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseCx2 == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_SENSE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_SENSE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMap(ssCompData.cx2_sn, 1,
+ ssCompData.header.sense_node,
+ thresholds_min, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS CX2 SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS CX2 SENSE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS CX2 SENSE ADJ TEST:\n");
+ if (todo->SelfSenseCx2Adj == 1) {
+ /* SS CX2 SENSE ADJH TEST */
+ pr_info("SS CX2 SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHoriz(ssCompData.cx2_sn, 1,
+ ssCompData.header.sense_node, &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS CX2 SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS CX2 SENSE ADJH computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjhor, 1,
+ ssCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS CX2 SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 SENSE ADJH TEST:.................OK\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+ } else
+ pr_info("SS CX2 SENSE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL CX SENSE */
+ pr_info("SS TOTAL CX SENSE TEST:\n");
+ if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) {
+ pr_info("SS TOTAL CX SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseCxTotal == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_SENSE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_SENSE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.cx_sn, 1,
+ totCompData.header.sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL CX SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL CX SENSE MIN MAX TEST:.................SKIPPED\n");
+
+
+ /* SS TOTAL IX SENSE ADJH TEST */
+ pr_info("SS TOTAL CX SENSE ADJ TEST:\n");
+ if (todo->SelfSenseCxTotalAdj == 1) {
+ pr_info("SS TOTAL CX SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizTotal(totCompData.cx_sn, 1,
+ totCompData.header.sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS TOTAL CX SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL CX SENSE ADJ HORIZ computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor, 1,
+ totCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS TOTAL CX SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+ } else
+ pr_info("SS TOTAL CX SENSE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL CX SENSE TEST:.................SKIPPED\n");
+
+ if ((todo->SelfSenseCx1LP | todo->SelfSenseCx2LP |
+ todo->SelfSenseCx2AdjLP | todo->SelfSenseCxTotalLP |
+ todo->SelfSenseCxTotalAdjLP | todo->SelfSenseIx1LP |
+ todo->SelfSenseIx2LP | todo->SelfSenseIx2AdjLP |
+ todo->SelfSenseIxTotalLP | todo->SelfSenseIxTotalAdjLP) == 1) {
+ ret = production_test_ss_ix_cx_lp(path_limits, stop_on_fail,
+ todo);
+ if (ret < OK) {
+ count_fail += 1;
+ pr_err("production_test_data: production_test_ss_ix_cx_lp failed... ERROR = %08X\n",
+ ret);
+ goto ERROR;
+ }
+ } else
+ pr_info("SS IX CX LP TEST:.................SKIPPED\n");
+
+ERROR:
+ 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;
+ kfree(totCompData.ix_fm);
+ totCompData.ix_fm = NULL;
+ kfree(totCompData.ix_sn);
+ totCompData.ix_sn = NULL;
+ kfree(totCompData.cx_fm);
+ totCompData.cx_fm = NULL;
+ kfree(totCompData.cx_sn);
+ totCompData.cx_sn = NULL;
+ pr_info("SS IX CX testes finished!.................OK\n\n");
+ return OK;
+ }
+
+ /* 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, 1),
+ ssCompData.header.force_node, 1);
+ print_frame_i8("SS Init Data Cx2_fm = ", array1dTo2d_i8(
+ ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1),
+ ssCompData.header.force_node, 1);
+ 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_i8("SS Init Data Cx2_sn = ", array1dTo2d_i8(
+ ssCompData.cx2_sn,
+ ssCompData.header.sense_node,
+ ssCompData.header.sense_node), 1,
+ ssCompData.header.sense_node);
+ print_frame_u16("TOT SS Init Data Ix_fm = ", array1dTo2d_u16(
+ totCompData.ix_fm,
+ totCompData.header.force_node, 1),
+ totCompData.header.force_node, 1);
+ print_frame_short("TOT SS Init Data Cx_fm = ",
+ array1dTo2d_short(totCompData.cx_fm,
+ totCompData.header.force_node,
+ 1),
+ totCompData.header.force_node, 1);
+ print_frame_u16("TOT SS Init Data Ix_sn = ", array1dTo2d_u16(
+ totCompData.ix_sn,
+ totCompData.header.sense_node,
+ totCompData.header.sense_node), 1,
+ totCompData.header.sense_node);
+ print_frame_short("TOT SS Init Data Cx_sn = ",
+ array1dTo2d_short(totCompData.cx_sn,
+ totCompData.header.sense_node,
+ totCompData.header.sense_node),
+ 1, totCompData.header.sense_node);
+ pr_err("SS IX CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (ssCompData.ix2_fm != NULL)
+ kfree(ssCompData.ix2_fm);
+ if (ssCompData.ix2_sn != NULL)
+ kfree(ssCompData.ix2_sn);
+ if (ssCompData.cx2_fm != NULL)
+ kfree(ssCompData.cx2_fm);
+ if (ssCompData.cx2_sn != NULL)
+ kfree(ssCompData.cx2_sn);
+ if (totCompData.ix_fm != NULL)
+ kfree(totCompData.ix_fm);
+ if (totCompData.ix_sn != NULL)
+ kfree(totCompData.ix_sn);
+ if (totCompData.cx_fm != NULL)
+ kfree(totCompData.cx_fm);
+ if (totCompData.cx_sn != NULL)
+ kfree(totCompData.cx_sn);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+
+
+ERROR_LIMITS:
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (ssCompData.ix2_fm != NULL)
+ kfree(ssCompData.ix2_fm);
+ if (ssCompData.ix2_sn != NULL)
+ kfree(ssCompData.ix2_sn);
+ if (ssCompData.cx2_fm != NULL)
+ kfree(ssCompData.cx2_fm);
+ if (ssCompData.cx2_sn != NULL)
+ kfree(ssCompData.cx2_sn);
+ if (totCompData.ix_fm != NULL)
+ kfree(totCompData.ix_fm);
+ if (totCompData.ix_sn != NULL)
+ kfree(totCompData.ix_sn);
+ if (totCompData.cx_fm != NULL)
+ kfree(totCompData.cx_fm);
+ if (totCompData.cx_sn != NULL)
+ kfree(totCompData.cx_sn);
+ return ret;
+}
+
+/**
+ * Perform all the tests selected in a TestTodo variable related to SS Init
+ * data for LP mode (touch, keys etc..)
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_ss_ix_cx_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int ret;
+ int count_fail = 0;
+
+ int *thresholds = NULL;
+ int trows, tcolumns;
+ int *thresholds_min = NULL;
+ int *thresholds_max = NULL;
+
+ SelfSenseData ssCompData;
+ TotSelfSenseData totCompData;
+
+ u8 *adjhor = NULL;
+ u8 *adjvert = NULL;
+
+ short container;
+
+ u16 *total_adjhor = NULL;
+ u16 *total_adjvert = NULL;
+
+ pr_info("SS LP IX CX testes are starting...\n");
+ ret = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH_IDLE, &ssCompData);
+ /* read the SS LP compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readSelfSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ ret = readTotSelfSenseCompensationData(LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE,
+ &totCompData);
+ /* read the TOT SS LP compensation data */
+ if (ret < 0) {
+ pr_err("production_test_data: readTotSelfSenseCompensationData failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ kfree(ssCompData.ix2_fm);
+ kfree(ssCompData.ix2_sn);
+ kfree(ssCompData.cx2_fm);
+ kfree(ssCompData.cx2_sn);
+ return ret | ERROR_PROD_TEST_DATA;
+ }
+
+ /************* SS FORCE IX LP **************/
+ /* SS IX1 LP FORCE TEST */
+ pr_info("SS IX1 LP FORCE TEST:\n");
+ if (todo->SelfForceIx1LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX1_LP_FORCE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX1_LP_FORCE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ container = (short)ssCompData.f_ix1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS IX1 LP FORCE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX1 LP FORCE TEST:.................OK\n\n");
+ } else
+ pr_info("SS IX1 LP FORCE TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ /* SS IX2 LP FORCE TEST */
+ pr_info("SS IX2 LP FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceIx2LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_LP_FORCE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_LP_FORCE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapFromU(ssCompData.ix2_fm,
+ ssCompData.header.force_node, 1,
+ thresholds_min,
+ thresholds_max); /* check the
+ * values with
+ * thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 LP FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 LP FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS IX2 LP FORCE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS IX2 LP FORCE ADJ TEST:\n");
+ if (todo->SelfForceIx2AdjLP == 1) {
+ /* SS IX2 FORCE ADJV TEST */
+ pr_info("SS IX2 LP FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertFromU(ssCompData.ix2_fm,
+ ssCompData.header.force_node, 1,
+ &adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS IX2 LP FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS IX2 LP FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_LP_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns); /* load the max
+ * thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node - 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node -
+ 1, 1, thresholds_max); /* check the
+ * values with
+ * thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 LP FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 LP FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("SS IX2 LP FORCE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL FORCE IX */
+ pr_info("SS TOTAL IX LP FORCE TEST:\n");
+ if (todo->SelfForceIxTotalLP == 1 || todo->SelfForceIxTotalAdjLP == 1) {
+ pr_info("SS TOTAL IX LP FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceIxTotalLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_FORCE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_FORCE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotalFromU(totCompData.ix_fm,
+ totCompData.header.
+ force_node, 1,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX LP FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX LP FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL IX LP FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS TOTAL IX LP FORCE ADJ TEST:\n");
+ if (todo->SelfForceIxTotalAdjLP == 1) {
+ /* SS TOTAL IX FORCE ADJV TEST */
+ pr_info("SS TOTAL IX LP FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertTotalFromU(totCompData.ix_fm,
+ totCompData.header.
+ force_node, 1,
+ &total_adjvert);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS TOTAL IX LP FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL IX LP FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.
+ force_node - 1, 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX LP FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX LP FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("SS TOTAL IX LP FORCE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL IX LP FORCE TEST:.................SKIPPED\n\n");
+
+
+ /************** SS SENSE IX LP **************/
+ /* SS IX1 LP SENSE TEST */
+ pr_info("SS IX1 LP SENSE TEST:\n");
+ if (todo->SelfSenseIx1LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX1_LP_SENSE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX1_LP_SENSE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.s_ix1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS IX1 SENSE LP TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX1 LP SENSE TEST:.................OK\n\n");
+ } else
+ pr_info("SS IX1 LP SENSE TEST:.................SKIPPED\n\n");
+
+ kfree(thresholds);
+ thresholds = NULL;
+ /* SS IX2 SENSE TEST */
+ pr_info("SS IX2 LP SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseIx2LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_LP_SENSE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_IX2_LP_SENSE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapFromU(ssCompData.ix2_sn, 1,
+ ssCompData.header.sense_node,
+ thresholds_min, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 LP SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 LP SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 LP SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS IX2 LP SENSE MIN MAX TEST:.................SKIPPED\n\n");
+
+ pr_info("SS IX2 LP SENSE ADJ TEST:\n");
+ if (todo->SelfSenseIx2AdjLP == 1) {
+ /* SS IX2 SENSE ADJH TEST */
+ pr_info("SS IX2 SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizFromU(ssCompData.ix2_sn, 1,
+ ssCompData.header.sense_node,
+ &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS IX2 SENSE ADJ HORIZ computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ 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)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjhor, 1,
+ ssCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS IX2 LP SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS IX2 LP SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS IX2 LP SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+ } else
+ pr_info("SS IX2 LP SENSE ADJ TEST:.................SKIPPED\n");
+
+ /* SS TOTAL IX SENSE */
+ pr_info("SS TOTAL IX LP SENSE TEST:\n");
+ if (todo->SelfSenseIxTotalLP == 1 || todo->SelfSenseIxTotalAdjLP == 1) {
+ pr_info("SS TOTAL IX LP SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseIxTotalLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_SENSE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_SENSE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotalFromU(totCompData.ix_sn, 1,
+ totCompData.header.
+ sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL IX LP SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX LP SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX LP SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL IX LP SENSE MIN MAX TEST:.................SKIPPED\n");
+
+
+ pr_info("SS TOTAL IX LP SENSE ADJ TEST:\n");
+ if (todo->SelfSenseIxTotalAdjLP == 1) {
+ /* SS TOTAL IX SENSE ADJH TEST */
+ pr_info("SS TOTAL IX LP SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizTotalFromU(totCompData.ix_sn, 1,
+ totCompData.header.
+ sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS TOTAL IX LP SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL IX LP SENSE ADJ HORIZ computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor, 1,
+ totCompData.header.
+ sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS TOTAL IX LP SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL IX LP SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL IX LP SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+ } else
+ pr_info("SS TOTAL IX LP SENSE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL IX LP SENSE TEST:.................SKIPPED\n");
+
+ /************* SS SENSE CX LP **************/
+ /* SS CX1 LP FORCE TEST */
+ pr_info("SS CX1 LP FORCE TEST:\n");
+ if (todo->SelfForceCx1LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX1_LP_FORCE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX1_LP_FORCE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.f_cx1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS CX1 LP FORCE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX1 LP FORCE TEST:.................OK\n\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS CX1 LP FORCE TEST:.................SKIPPED\n\n");
+
+
+
+ /* SS CX2 LP FORCE TEST */
+ pr_info("SS CX2 LP FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceCx2LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_FORCE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_FORCE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMap(ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS CX2 LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 LP FORCE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 LP FORCE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS CX2 LP FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS CX2 LP FORCE ADJ TEST:\n");
+ if (todo->SelfForceCx2AdjLP == 1) {
+ /* SS CX2 FORCE ADJV TEST */
+ pr_info("SS CX2 LP FORCE ADJVERT TEST:\n");
+ ret = computeAdjVert(ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1, &adjvert);
+ /* compute the ADJV for CX2 FORCE */
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS CX2 LP FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS CX2 LP FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != ssCompData.header.force_node - 1 ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node -
+ 1, 1, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 LP FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 LP FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjvert);
+ adjvert = NULL;
+ } else
+ pr_info("SS CX2 LP FORCE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL CX LP FORCE */
+ pr_info("SS TOTAL CX LP FORCE TEST:\n");
+ if (todo->SelfForceCxTotalLP == 1 || todo->SelfForceCxTotalAdjLP == 1) {
+ pr_info("SS TOTAL CX LP FORCE MIN MAX TEST:\n");
+ if (todo->SelfForceCxTotalLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_FORCE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_FORCE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows !=
+ totCompData.header.force_node ||
+ tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.cx_fm,
+ totCompData.header.force_node,
+ 1, thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL FORCE LP MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL FORCE LP MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL CX LP FORCE MIN MAX TEST:.................SKIPPED\n");
+
+ /* SS TOTAL CX LP FORCE ADJV TEST */
+ pr_info("SS TOTAL CX LP FORCE ADJ TEST:\n");
+ if (todo->SelfForceCxTotalAdjLP == 1) {
+ pr_info("SS TOTAL CX LP FORCE ADJVERT TEST:\n");
+ ret = computeAdjVertTotal(totCompData.cx_fm,
+ totCompData.header.force_node,
+ 1, &total_adjvert);
+ /* compute the ADJV for CX2 FORCE */
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjVert SS TOTAL CX LP FORCE ADJV failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL CX LP FORCE ADJV computed!\n");
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != totCompData.header.force_node -
+ 1 || tcolumns != 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjvert,
+ totCompData.header.
+ force_node - 1, 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL CX LP FORCE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX LP FORCE ADJV TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX LP FORCE ADJV TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjvert);
+ total_adjvert = NULL;
+ } else
+ pr_info("SS TOTAL CX LP FORCE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL CX LP FORCE TEST:.................SKIPPED\n\n");
+
+
+
+ /************* SS SENSE CX *************/
+ /* SS CX1 SENSE TEST */
+ pr_info("SS CX1 LP SENSE TEST:\n");
+ if (todo->SelfSenseCx1LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX1_LP_SENSE_MIN_MAX,
+ &thresholds, &trows, &tcolumns);
+ if (ret < 0 || (trows != 1 || tcolumns != 2)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX1_LP_SENSE_MIN_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ container = (short)ssCompData.s_cx1;
+ ret = checkLimitsMinMax(&container, 1, 1, thresholds[0],
+ thresholds[1]);
+ /* check the limits */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMinMax SS CX1 LP SENSE TEST failed... ERROR COUNT = %d\n",
+ ret);
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX1 LP SENSE TEST:.................OK\n\n");
+ kfree(thresholds);
+ thresholds = NULL;
+ } else
+ pr_info("SS CX1 LP SENSE TEST:.................SKIPPED\n\n");
+
+
+ /* SS CX2 LP SENSE TEST */
+ pr_info("SS CX2 LP SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseCx2LP == 1) {
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_SENSE_MAP_MIN,
+ &thresholds_min, &trows,
+ &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_LP_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_SENSE_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_CX2_LP_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMap(ssCompData.cx2_sn, 1,
+ ssCompData.header.sense_node,
+ thresholds_min, thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS CX2 LP SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 LP SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 LP SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS CX2 LP SENSE MIN MAX TEST:.................SKIPPED\n");
+
+ pr_info("SS CX2 LP SENSE ADJ TEST:\n");
+ if (todo->SelfSenseCx2AdjLP == 1) {
+ /* SS CX2 SENSE ADJH TEST */
+ pr_info("SS CX2 LP SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHoriz(ssCompData.cx2_sn, 1,
+ ssCompData.header.sense_node, &adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS CX2 LP SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS CX2 LP SENSE ADJH computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits, &limit_file,
+ SS_CX2_LP_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ ssCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdj(adjhor, 1,
+ ssCompData.header.sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS CX2 LP SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS CX2 LP SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS CX2 LP SENSE ADJH TEST:.................OK\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(adjhor);
+ adjhor = NULL;
+ } else
+ pr_info("SS CX2 LP SENSE ADJ TEST:.................SKIPPED\n\n");
+
+ /* SS TOTAL CX SENSE */
+ pr_info("SS TOTAL CX LP SENSE TEST:\n");
+ if (todo->SelfSenseCxTotalLP == 1 || todo->SelfSenseCxTotalAdjLP == 1) {
+ pr_info("SS TOTAL CX LP SENSE MIN MAX TEST:\n");
+ if (todo->SelfSenseCxTotalLP == 1) {
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_SENSE_MAP_MIN,
+ &thresholds_min,
+ &trows, &tcolumns);
+ /* load the min thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_MAP_MIN failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_SENSE_MAP_MAX,
+ &thresholds_max,
+ &trows, &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapTotal(totCompData.cx_sn, 1,
+ totCompData.header.sense_node,
+ thresholds_min,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMap SS TOTAL CX LP SENSE failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX LP SENSE MIN MAX TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX LP SENSE MIN MAX TEST:.................OK\n\n");
+
+ kfree(thresholds_min);
+ thresholds_min = NULL;
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ } else
+ pr_info("SS TOTAL CX LP SENSE MIN MAX TEST:.................SKIPPED\n");
+
+
+ /* SS TOTAL IX SENSE ADJH TEST */
+ pr_info("SS TOTAL CX LP SENSE ADJ TEST:\n");
+ if (todo->SelfSenseCxTotalAdjLP == 1) {
+ pr_info("SS TOTAL CX LP SENSE ADJHORIZ TEST:\n");
+ ret = computeAdjHorizTotal(totCompData.cx_sn, 1,
+ totCompData.header.sense_node,
+ &total_adjhor);
+ if (ret < 0) {
+ pr_err("production_test_data: computeAdjHoriz SS TOTAL CX LP SENSE ADJH failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+ pr_info("SS TOTAL CX LP SENSE ADJ HORIZ computed!\n");
+
+
+ ret = parseProductionTestLimits(path_limits,
+ &limit_file,
+ SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX,
+ &thresholds_max, &trows,
+ &tcolumns);
+ /* load the max thresholds */
+ if (ret < 0 || (trows != 1 || tcolumns !=
+ totCompData.header.sense_node - 1)) {
+ pr_err("production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n",
+ ERROR_PROD_TEST_DATA);
+ ret |= ERROR_PROD_TEST_DATA;
+ goto ERROR_LIMITS;
+ }
+
+ ret = checkLimitsMapAdjTotal(total_adjhor, 1,
+ totCompData.header.
+ sense_node - 1,
+ thresholds_max);
+ /* check the values with thresholds */
+ if (ret != OK) {
+ pr_err("production_test_data: checkLimitsMapAdj SS TOTAL CX LP SENSE ADJH failed... ERROR COUNT = %d\n",
+ ret);
+ pr_err("SS TOTAL CX LP SENSE ADJH TEST:.................FAIL\n\n");
+ count_fail += 1;
+ if (stop_on_fail)
+ goto ERROR;
+ } else
+ pr_info("SS TOTAL CX LP SENSE ADJH TEST:.................OK\n\n");
+
+ kfree(thresholds_max);
+ thresholds_max = NULL;
+ kfree(total_adjhor);
+ total_adjhor = NULL;
+ } else
+ pr_info("SS TOTAL CX LP SENSE ADJ TEST:.................SKIPPED\n");
+ } else
+ pr_info("SS TOTAL CX LP SENSE TEST:.................SKIPPED\n");
+
+
+
+ERROR:
+
+ 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;
+ kfree(totCompData.ix_fm);
+ totCompData.ix_fm = NULL;
+ kfree(totCompData.ix_sn);
+ totCompData.ix_sn = NULL;
+ kfree(totCompData.cx_fm);
+ totCompData.cx_fm = NULL;
+ kfree(totCompData.cx_sn);
+ totCompData.cx_sn = NULL;
+ pr_info("SS LP IX CX testes finished!.................OK\n\n");
+ return OK;
+ } else {
+ /* print all kind of data in just one row for readability reason */
+ print_frame_u8("SS LP Init Data Ix2_fm = ", array1dTo2d_u8(
+ ssCompData.ix2_fm,
+ ssCompData.header.force_node, 1),
+ ssCompData.header.force_node, 1);
+ print_frame_i8("SS LP Init Data Cx2_fm = ", array1dTo2d_i8(
+ ssCompData.cx2_fm,
+ ssCompData.header.force_node, 1),
+ ssCompData.header.force_node, 1);
+ print_frame_u8("SS LP Init Data Ix2_sn = ", array1dTo2d_u8(
+ ssCompData.ix2_sn,
+ ssCompData.header.sense_node,
+ ssCompData.header.sense_node), 1,
+ ssCompData.header.sense_node);
+ print_frame_i8("SS LP Init Data Cx2_sn = ", array1dTo2d_i8(
+ ssCompData.cx2_sn,
+ ssCompData.header.sense_node,
+ ssCompData.header.sense_node), 1,
+ ssCompData.header.sense_node);
+ print_frame_u16("TOT SS LP Init Data Ix_fm = ", array1dTo2d_u16(
+ totCompData.ix_fm,
+ totCompData.header.force_node, 1),
+ totCompData.header.force_node, 1);
+ print_frame_short("TOT SS LP Init Data Cx_fm = ",
+ array1dTo2d_short(totCompData.cx_fm,
+ totCompData.header.
+ force_node, 1),
+ totCompData.header.force_node, 1);
+ print_frame_u16("TOT SS LP Init Data Ix_sn = ", array1dTo2d_u16(
+ totCompData.ix_sn,
+ totCompData.header.sense_node,
+ totCompData.header.sense_node), 1,
+ totCompData.header.sense_node);
+ print_frame_short("TOT SS LP Init Data Cx_sn = ",
+ array1dTo2d_short(totCompData.cx_sn,
+ totCompData.header.
+ sense_node,
+ totCompData.header.
+ sense_node),
+ 1, totCompData.header.sense_node);
+ pr_err("SS LP IX CX testes finished!.................FAILED fails_count = %d\n\n",
+ count_fail);
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (ssCompData.ix2_fm != NULL)
+ kfree(ssCompData.ix2_fm);
+ if (ssCompData.ix2_sn != NULL)
+ kfree(ssCompData.ix2_sn);
+ if (ssCompData.cx2_fm != NULL)
+ kfree(ssCompData.cx2_fm);
+ if (ssCompData.cx2_sn != NULL)
+ kfree(ssCompData.cx2_sn);
+ if (totCompData.ix_fm != NULL)
+ kfree(totCompData.ix_fm);
+ if (totCompData.ix_sn != NULL)
+ kfree(totCompData.ix_sn);
+ if (totCompData.cx_fm != NULL)
+ kfree(totCompData.cx_fm);
+ if (totCompData.cx_sn != NULL)
+ kfree(totCompData.cx_sn);
+ return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA;
+ }
+
+ERROR_LIMITS:
+ if (thresholds != NULL)
+ kfree(thresholds);
+ if (thresholds_min != NULL)
+ kfree(thresholds_min);
+ if (thresholds_max != NULL)
+ kfree(thresholds_max);
+ if (adjhor != NULL)
+ kfree(adjhor);
+ if (adjvert != NULL)
+ kfree(adjvert);
+ if (total_adjhor != NULL)
+ kfree(total_adjhor);
+ if (total_adjvert != NULL)
+ kfree(total_adjvert);
+ if (ssCompData.ix2_fm != NULL)
+ kfree(ssCompData.ix2_fm);
+ if (ssCompData.ix2_sn != NULL)
+ kfree(ssCompData.ix2_sn);
+ if (ssCompData.cx2_fm != NULL)
+ kfree(ssCompData.cx2_fm);
+ if (ssCompData.cx2_sn != NULL)
+ kfree(ssCompData.cx2_sn);
+ if (totCompData.ix_fm != NULL)
+ kfree(totCompData.ix_fm);
+ if (totCompData.ix_sn != NULL)
+ kfree(totCompData.ix_sn);
+ if (totCompData.cx_fm != NULL)
+ kfree(totCompData.cx_fm);
+ if (totCompData.cx_sn != NULL)
+ kfree(totCompData.cx_sn);
+ return ret;
+}
+
+/**
+ * Perform a complete Data Test check of the IC
+ * @param path_limits name of Production Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param stop_on_fail if 1, the test flow stops at the first data check
+ * failure
+ * otherwise it keeps going performing all the selected test
+ * @param todo pointer to a TestToDo variable which select the test to do
+ * @return OK if success or an error code which specify the type of error
+ */
+int production_test_data(const char *path_limits, int stop_on_fail,
+ TestToDo *todo)
+{
+ int res = OK, ret;
+
+ if (todo == NULL) {
+ pr_err("production_test_data: No TestToDo specified!! ERROR = %08X\n",
+ (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA));
+ return ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA;
+ }
+
+
+ pr_info("DATA Production test is starting...\n");
+
+
+ ret = production_test_ms_raw(path_limits, stop_on_fail, todo);
+ res |= ret;
+ if (ret < 0) {
+ pr_err("production_test_data: production_test_ms_raw failed... ERROR = %08X\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) {
+ pr_err("production_test_data: production_test_ms_cx failed... ERROR = %08X\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) {
+ pr_err("production_test_data: production_test_ss_raw failed... ERROR = %08X\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) {
+ pr_err("production_test_data: production_test_ss_ix_cx failed... ERROR = %08X\n",
+ ret);
+ if (stop_on_fail == 1)
+ goto END;
+ }
+
+END:
+ freeLimitsFile(&limit_file); /* /< release the limit file loaded
+ * during the test */
+ if (res < OK)
+ pr_err("DATA Production test failed!\n");
+ else
+ pr_info("DATA Production test finished!\n");
+ return res;
+}
+
+
+/*************** TP Sensitivity calibration API ********************/
+
+/**
+ * Perform the Pre Calibration MS Test when the stimpad is down
+ * @param[out] frame pointer to the frame which will contain
+ * the average frame resulting from the test
+ * @param target reference value for the frame, each node should be
+ * around +-percentage% this value
+ * @param percentage percentage of the target value which define
+ * the valid interval for the frame, if <0 the test will be skipped
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_test_pre_cal_ms(MutualSenseFrame *finalFrame, short target,
+ int percentage)
+{
+ int ret = OK;
+ int count = 0, i = 0, j = 0;
+ short min, max;
+ MutualSenseFrame frame;
+
+ finalFrame->node_data = NULL;
+
+
+ pr_info("%s: Start TP sensitivity MS Pre Cal...\n", __func__);
+ pr_info("%s: IMPORTANT!!! Stimpad should be on the display of the device!\n",
+ __func__);
+ ret = getMSFrame3(MS_STRENGTH, &frame);
+ if (ret < OK) {
+ pr_err("%s: can not read MS Frame... ERROR %08X\n",
+ __func__, ret);
+ goto ERROR;
+ }
+
+ finalFrame->header = frame.header;
+ finalFrame->node_data_size = frame.node_data_size;
+
+ finalFrame->node_data = (short *)kzalloc(frame.node_data_size *
+ sizeof(short), GFP_KERNEL);
+ if (finalFrame->node_data == NULL) {
+ pr_err("%s: can not allocate node_data ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ ret = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* collecting frames */
+ do {
+ for (i = 0; i < finalFrame->node_data_size; i++) {
+ finalFrame->node_data[i] += (frame.node_data[i] * 10) /
+ SENS_TEST_NUM_FRAMES;
+ }
+
+ if (frame.node_data != NULL) {
+ kfree(frame.node_data);
+ frame.node_data = NULL;
+ }
+
+ count++;
+
+ /* exclude one more reading at the end*/
+ if (count < SENS_TEST_NUM_FRAMES)
+ ret = getMSFrame3(MS_STRENGTH, &frame);
+ } while ((count < SENS_TEST_NUM_FRAMES) && (ret >= OK));
+
+ if (ret < OK) {
+ pr_err("%s: Error while capturing the frame %d! ERROR %08X\n",
+ __func__, count, ret);
+ goto ERROR;
+ }
+
+ ret = OK;
+ /* check against +-percentage% target */
+ pr_info("%s: Computing average frame...\n", __func__);
+
+ min = target - (target * percentage / 100);
+ max = target + (target * percentage / 100);
+
+ for (i = 0; i < finalFrame->header.force_node; i++) {
+ for (j = 0; j < finalFrame->header.sense_node; j++) {
+ finalFrame->node_data[i *
+ finalFrame->header.sense_node +
+ j] /= 10;
+ /*if percentage is <0 skip this test, just collect data */
+ if ((percentage > 0) &&
+ ((finalFrame->node_data[i * finalFrame->header.
+ sense_node
+ + j] >
+ max) ||
+ (finalFrame->node_data[i *
+ finalFrame->header.
+ sense_node
+ + j] <
+ min))) {
+ pr_err("%s: MS Force Node[%d, %d] = %d exceed limit [%d, %d]\n",
+ __func__, i, j,
+ finalFrame->node_data[i *
+ finalFrame
+ ->header.
+ sense_node + j],
+ min, max);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+ }
+ }
+
+
+ /* print average frame in the log */
+ print_frame_short("MS FS Mean =",
+ array1dTo2d_short(
+ finalFrame->node_data,
+ finalFrame->node_data_size,
+ finalFrame->header.sense_node),
+ finalFrame->header.force_node,
+ finalFrame->header.sense_node);
+
+ if (ret != OK)
+ pr_err("%s: TP sensitivity MS Pre Cal test FAILED... ERROR %08X\n",
+ __func__, ret);
+ else
+ pr_info("%s: TP sensitivity MS Pre Cal FINISHED!\n",
+ __func__);
+
+ return ret;
+
+
+ERROR:
+ if (frame.node_data != NULL) {
+ kfree(frame.node_data);
+ frame.node_data = NULL;
+ }
+
+
+ if (finalFrame->node_data != NULL) {
+ kfree(finalFrame->node_data);
+ finalFrame->node_data = NULL;
+ }
+
+ return ret;
+}
+
+
+
+/**
+ * Perform the Pre Calibration SS Test when the stimpad is down
+ * @param[out] frame pointer to the frame which will contain the average frame
+ * resulting from the test
+ * @param target reference value for the frame, each node should be around
+ * +-percentage% this value
+ * @param percentage percentage of the target value which define the valid
+ * interval for the frame
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_test_pre_cal_ss(SelfSenseFrame *finalFrame, short target,
+ int percentage)
+{
+ int ret = OK;
+ int count = 0, i = 0;
+ short min, max;
+ SelfSenseFrame frame;
+ int *temp_force = NULL;
+ int *temp_sense = NULL;
+
+ finalFrame->force_data = NULL;
+ finalFrame->sense_data = NULL;
+
+ pr_info("%s: Start TP sensitivity SS Pre Cal...\n", __func__);
+ pr_info("%s: IMPORTANT!!! Stimpad should be on the display of the device!\n",
+ __func__);
+ ret = getSSFrame3(SS_STRENGTH, &frame);
+ if (ret < OK) {
+ pr_err("%s: can not read SS Frame... ERROR %08X\n",
+ __func__, ret);
+ goto ERROR;
+ }
+
+ finalFrame->header = frame.header;
+
+ finalFrame->force_data = (short *)kzalloc(frame.header.force_node *
+ sizeof(short), GFP_KERNEL);
+ temp_force = (int *)kzalloc(frame.header.force_node *
+ sizeof(int), GFP_KERNEL);
+ finalFrame->sense_data = (short *)kzalloc(frame.header.sense_node *
+ sizeof(short), GFP_KERNEL);
+ temp_sense = (int *)kzalloc(frame.header.sense_node *
+ sizeof(int), GFP_KERNEL);
+ if (finalFrame->force_data == NULL ||
+ temp_force == NULL ||
+ finalFrame->sense_data == NULL ||
+ temp_sense == NULL) {
+
+ pr_err("%s: can not allocate memory ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ ret = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* collecting frames */
+ do {
+ for (i = 0; i < finalFrame->header.force_node; i++)
+ temp_force[i] += frame.force_data[i];
+
+ for (i = 0; i < finalFrame->header.sense_node; i++)
+ temp_sense[i] += frame.sense_data[i];
+
+ count++;
+
+ if (frame.force_data != NULL) {
+ kfree(frame.force_data);
+ frame.force_data = NULL;
+ }
+ if (frame.sense_data != NULL) {
+ kfree(frame.sense_data);
+ frame.sense_data = NULL;
+ }
+
+ /* exclude one more reading at the end*/
+ if (count < SENS_TEST_NUM_FRAMES)
+ ret = getSSFrame3(SS_STRENGTH, &frame);
+ } while ((count < SENS_TEST_NUM_FRAMES) && (ret >= OK));
+
+ if (ret < OK) {
+ pr_err("%s: Error while capturing the frame %d! ERROR %08X\n",
+ __func__, count, ret);
+ goto ERROR;
+ }
+
+ ret = OK;
+
+ /* compute the average and check against +-percentage% target */
+ min = target - (target * percentage / 100);
+ max = target + (target * percentage / 100);
+
+ for (i = 0; i < finalFrame->header.force_node; i++) {
+ finalFrame->force_data[i] = temp_force[i] /
+ SENS_TEST_NUM_FRAMES;
+ if ((percentage > 0) && ((finalFrame->force_data[i] > max) ||
+ (finalFrame->force_data[i] < min))) {
+ pr_err("%s: SS Force Node[%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, finalFrame->force_data[i],
+ min, max);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+ }
+
+ for (i = 0; i < finalFrame->header.sense_node; i++) {
+ finalFrame->sense_data[i] = temp_sense[i] /
+ SENS_TEST_NUM_FRAMES;
+ if ((finalFrame->sense_data[i] > max) ||
+ (finalFrame->sense_data[i] < min)) {
+ pr_err("%s: SS Sense Node[%d] = %d exceed limit [%d, %d]\n",
+ __func__, i, finalFrame->sense_data[i],
+ min, max);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+ }
+
+ /* print average frame in the log */
+ print_frame_short("SS FS force Mean =",
+ array1dTo2d_short(
+ finalFrame->force_data,
+ finalFrame->header.force_node,
+ 1),
+ finalFrame->header.force_node, 1);
+ print_frame_short("SS FS sense Mean =",
+ array1dTo2d_short(
+ finalFrame->sense_data,
+ finalFrame->header.sense_node,
+ finalFrame->header.sense_node),
+ 1, finalFrame->header.sense_node);
+
+
+ kfree(temp_force);
+ temp_force = NULL;
+
+ kfree(temp_sense);
+ temp_sense = NULL;
+
+ if (ret < OK)
+ pr_err("%s: TP sensitivity SS Pre Cal test FAILED... ERROR %08X\n",
+ __func__, ret);
+ else {
+ pr_info("%s: TP sensitivity SS Pre Cal FINISHED!\n",
+ __func__);
+ ret = OK;
+ }
+
+ return ret;
+
+
+ERROR:
+
+ kfree(temp_force);
+ temp_force = NULL;
+
+ kfree(temp_sense);
+ temp_sense = NULL;
+
+ kfree(frame.force_data);
+ frame.force_data = NULL;
+
+ kfree(frame.sense_data);
+ frame.sense_data = NULL;
+
+ kfree(finalFrame->force_data);
+ finalFrame->force_data = NULL;
+
+ kfree(finalFrame->sense_data);
+ finalFrame->sense_data = NULL;
+
+ return ret;
+}
+
+/**
+ * Compute Digital gains for calibration
+ * @param frame pointer to the frame used as reference to compute the gains
+ * @param target reference target value for computing the gains
+ * @param saveGain if 1, will save the gain table into the chip otherwise will
+ * not save it
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_compute_gains(MutualSenseFrame *frame, short target,
+ int saveGain)
+{
+ int ret = OK;
+ int i = 0;
+ u8 *gains;
+
+ if ((frame->node_data == NULL) || (frame->node_data_size == 0)) {
+ pr_err("%s: Invalid frame data passed as argument! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ gains = kzalloc(frame->node_data_size * sizeof(u8), GFP_KERNEL);
+ if (gains == NULL)
+ return ERROR_ALLOC;
+
+ pr_info("%s: Start to compute Digital Gains...\n", __func__);
+ for (i = 0; i < frame->node_data_size; i++)
+ gains[i] = ((target * 100) / frame->node_data[i]) > 255 ?
+ (u8)(255) : (u8)(((target * 100) /
+ frame->node_data[i]));
+ /* clamp the max value to 255 because gain is only one byte */
+
+
+ /* print average frame in the log */
+ print_frame_u8("MS Digital Gain =",
+ array1dTo2d_u8(
+ gains,
+ frame->node_data_size,
+ frame->header.sense_node),
+ frame->header.force_node,
+ frame->header.sense_node);
+
+
+ /* if(saveGain==1){ */
+ /* write gains into the IC */
+ ret = writeHostDataMemory(LOAD_SENS_CAL_COEFF, gains,
+ frame->header.force_node,
+ frame->header.sense_node, 0, 0, saveGain);
+ if (ret != OK)
+ pr_err("%s: impossible to write digital gains! ERROR %08X\n",
+ __func__, ret);
+ /* } */
+
+ if (ret < OK)
+ pr_err("%s: compute Digital Gains FAILED! ERROR %08X\n",
+ __func__, ret);
+ else {
+ pr_info("%s: compute Digital Gains FINISHED!\n", __func__);
+ ret = OK;
+ }
+
+ kfree(gains);
+ return ret;
+}
+
+/**
+ * Perform the Post Calibration MS Test when the stimpad is down
+ * @param[out] finalFrame pointer to the frame which will contain
+ * the average frame resulting from the test
+ * @param[out] deltas pointer to the frame which will contain
+ * the FS Uniform frame (worst_neighborhood/mean)
+ * @param target reference value for the frame, each node should be
+ * around +-percentage% this value
+ * @param percentage percentage of the target value which define
+ * the valid interval for the frame, if <0 the test will be skipped
+ * @param[out] mean_normal pointer to the variable which will contain the mean
+ * of the normal area
+ * @param[out] mean_edge pointer to the variable which will contain the mean of
+ * the edge area
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_test_post_cal_ms(MutualSenseFrame *finalFrame,
+ MutualSenseFrame *deltas, short target,
+ int percentage, int *mean_normal,
+ int *mean_edge)
+{
+ short currentNode;
+ int final_force_num;
+ int final_sense_num;
+ short *final_node;
+ int delta_sense_num;
+ short *delta_node;
+ short *delta;
+ short adjNode;
+ int ret = OK;
+ int i = 0, j = 0, min, max;
+
+
+ if ((finalFrame == NULL) || (deltas == NULL) || (mean_normal == NULL) ||
+ (mean_edge == NULL)) {
+ pr_err("%s: Invalid arguments Passed! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ *mean_normal = 0;
+ *mean_edge = 0;
+
+ finalFrame->node_data = NULL;
+ deltas->node_data = NULL;
+
+ pr_info("%s: Start TP sensitivity MS Post Cal...\n", __func__);
+ pr_info("%s: IMPORTANT!!! Stimpad should be on the display of the device!\n",
+ __func__);
+
+ /* collect frames skipping the tests + print on the log */
+ ret = tp_sensitivity_test_pre_cal_ms(finalFrame, target, -1);
+ if (ret < OK) {
+ pr_err("%s: can not collect MS Frame... ERROR %08X\n",
+ __func__, ret);
+ goto ERROR;
+ }
+
+
+ deltas->header = finalFrame->header;
+ deltas->node_data_size = finalFrame->node_data_size;
+
+ deltas->node_data = (short *)kzalloc(deltas->node_data_size *
+ sizeof(short), GFP_KERNEL);
+ if (deltas->node_data == NULL) {
+ pr_err("%s: can not allocate deltas node_data ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ ret = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* compute the average of the whole panel and check against
+ * +-percentage% target */
+ pr_info("%s: Computing average of whole panel and delta for each node...\n",
+ __func__);
+
+ final_force_num = finalFrame->header.force_node;
+ final_sense_num = finalFrame->header.sense_node;
+ final_node = finalFrame->node_data;
+ delta_sense_num = deltas->header.sense_node;
+ delta_node = deltas->node_data;
+
+
+ for (i = 0; i < final_force_num; i++) {
+ for (j = 0; j < final_sense_num; j++) {
+ currentNode = finalFrame->node_data[i *
+ finalFrame->header.
+ sense_node + j];
+ delta = &delta_node[i * delta_sense_num + j];
+
+ if ((i == 0) ||
+ (i == (final_force_num - 1)) ||
+ (j == 0) ||
+ (j == (final_sense_num - 1))) {
+ /* edge nodes */
+ *mean_edge += currentNode;
+ if ((i == 0) ||
+ (i == final_force_num - 1)) {
+ /* need to check adj node up or down for
+ * nodes in the corners */
+ if ((i == 0) &&
+ ((j == 0) ||
+ (j == final_sense_num - 1))) {
+ adjNode = currentNode -
+ final_node[(i + 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ if ((i == (final_force_num - 1)) &&
+ ((j == 0) ||
+ (j == final_sense_num - 1))) {
+ adjNode = currentNode -
+ final_node[(i - 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ /* scan the row */
+ if ((j - 1) >= 0) {
+ adjNode = currentNode -
+ final_node[i *
+ final_sense_num +
+ (j - 1)];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ if ((j + 1) < final_sense_num) {
+ adjNode = currentNode -
+ final_node[i *
+ final_sense_num +
+ (j + 1)];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+ }
+
+ if ((j == 0) ||
+ (j == final_sense_num - 1)) {
+ /* scan the column */
+ if ((i - 1) >= 0) {
+ adjNode = currentNode -
+ final_node[(i - 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ if ((i + 1) < final_force_num) {
+ adjNode = currentNode -
+ final_node[(i + 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+ }
+ } else {
+ /*normal nodes */
+ *mean_normal += currentNode;
+
+ /* picking up the worst difference between
+ * one pixel and its neighbors */
+ if ((i - 1) >= 1) {
+ adjNode = currentNode -
+ final_node[(i - 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ if ((i + 1) < (final_force_num - 1)) {
+ adjNode = currentNode -
+ final_node[(i + 1) *
+ final_sense_num + j];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+ if ((j - 1) >= 1) {
+ adjNode = currentNode -
+ final_node[i *
+ final_sense_num + (j - 1)];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+
+ if ((j + 1) < (final_sense_num - 1)) {
+ adjNode = currentNode -
+ final_node[i *
+ final_sense_num + (j + 1)];
+ if (abs(adjNode) > *delta)
+ *delta = abs(adjNode);
+ }
+ }
+ }
+ }
+
+ *mean_normal /= (finalFrame->header.force_node - 2) *
+ (finalFrame->header.sense_node - 2);
+ *mean_edge /= (finalFrame->header.force_node * 2) +
+ (finalFrame->header.sense_node - 2) * 2;
+
+ pr_info("%s: Normal Frame average = %d\n", __func__, *mean_normal);
+ pr_info("%s: Edge Frame average = %d\n", __func__, *mean_edge);
+ /* compute the average and check against +-% target */
+ min = target - (target * percentage / 100);
+ max = target + (target * percentage / 100);
+
+ if ((percentage > 0) && ((*mean_normal < min) || (*mean_normal >
+ max))) {
+ pr_err("%s: Normal Frame average = %d exceed limit [%d, %d]\n",
+ __func__, *mean_normal, min, max);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+
+ if ((percentage > 0) && ((*mean_edge < min) || (*mean_edge > max))) {
+ pr_err("%s: Edge Frame average = %d exceed limit [%d, %d]\n",
+ __func__, *mean_edge, min, max);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+
+ for (i = 0; i < deltas->header.force_node; i++) {
+ for (j = 0; j < deltas->header.sense_node; j++) {
+ if ((i == 0) || (i == deltas->header.force_node) ||
+ (j == 0) || (j == deltas->header.sense_node))
+ deltas->node_data[i *
+ deltas->header.sense_node +
+ j] =
+ deltas->node_data[i *
+ deltas->header.
+ sense_node + j] *
+ 100 /
+ (*mean_edge);
+ else
+ deltas->node_data[i *
+ deltas->header.sense_node +
+ j] =
+ deltas->node_data[i *
+ deltas->header.
+ sense_node + j] *
+ 100 /
+ (*mean_normal);
+
+ if ((percentage > 0) && (deltas->node_data[i *
+ deltas->
+ header.
+ sense_node +
+ j] >
+ percentage)) {
+ pr_err("%s: Delta Node[%d, %d] = %d exceed limit [%d]\n",
+ __func__, i, j,
+ deltas->node_data[i *
+ deltas
+ ->header.sense_node +
+ j], percentage);
+ ret = ERROR_TEST_CHECK_FAIL;
+ }
+ }
+ }
+
+
+ /* print average frame in the log */
+ print_frame_short("FS Uniform (%) =",
+ array1dTo2d_short(
+ deltas->node_data,
+ deltas->node_data_size,
+ deltas->header.sense_node),
+ deltas->header.force_node,
+ deltas->header.sense_node);
+
+
+ if (ret < OK)
+ pr_err("%s: TP sensitivity MS Post Cal test FAILED... ERROR %08X\n",
+ __func__, ret);
+ else {
+ pr_info("%s: TP sensitivity MS Post Cal FINISHED!\n",
+ __func__);
+ ret = OK;
+ }
+
+ return ret;
+
+
+ERROR:
+ if (deltas->node_data != NULL) {
+ kfree(deltas->node_data);
+ deltas->node_data = NULL;
+ }
+
+
+ if (finalFrame->node_data != NULL) {
+ kfree(finalFrame->node_data);
+ finalFrame->node_data = NULL;
+ }
+
+ return ret;
+}
+
+
+/**
+ * Compute Digital gains for calibration
+ * @param enter if =1 turn on TP Sensitivity mode, otherwise will turn it off
+ * @param saveGain if 1, will save the gain table into the chip otherwise will
+ * not save it
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_mode(u8 enter, int saveGain)
+{
+ int res, ret = OK;
+ u8 cmd[4] = { 0xC0, 0x00, 0x00, 0x00 };
+ u8 sett = SPECIAL_WRITE_HOST_MEM_TO_FLASH;
+ u8 parameter[2] = { 0x00, 0x01 };
+
+ pr_info("%s: Start TP Sensitivity Mode... enter = %02X\n",
+ __func__, enter);
+ if (enter == 1) {
+ /* enter TP Sensitivity mode*/
+ ret = fts_enableInterrupt(false);
+ pr_info("%s: Entering TP Sensitivity Mode disabling algos...\n",
+ __func__);
+ cmd[3] = 0x01;
+ res = fts_writeFwCmd(cmd, 4);
+ if (res < OK)
+ pr_err("%s: Error while turning on TP Sens Mode! ERROR %08X\n",
+ __func__, res);
+ else {
+ ret = writeSysCmd(SYS_CMD_CX_TUNING, parameter, 2);
+ if (ret < OK)
+ pr_err("%s: error while performing Single Ended Special Autotune! ERROR %08X\n",
+ __func__, ret);
+ }
+ } else {
+ /* exit TP Sensitivity mode*/
+ pr_info("%s: Exiting TP Sensitivity Mode enabling algos...\n",
+ __func__);
+ res = fts_writeFwCmd(cmd, 4);
+ if (res < OK)
+ pr_err("%s: Error while turning off TP Sens Mode! ERROR %08X\n",
+ __func__, res);
+
+ if (saveGain == 1) {
+ pr_info("%s: Trigger writing gains into the flash...\n",
+ __func__);
+ ret = writeSysCmd(SYS_CMD_SPECIAL, &sett, 1);
+ if (ret < OK)
+ pr_err("%s: error while writing gains into the flash! ERROR %08X\n",
+ __func__, res);
+ }
+
+ res |= senseOn();
+ res |= fts_enableInterrupt(true);
+ }
+
+ res |= ret;
+
+ if (res < OK)
+ pr_err("%s: TP Sensitivity Mode... ERROR %08X!\n",
+ __func__, res);
+ else
+ pr_info("%s: TP Sensitivity Mode FINISHED!\n", __func__);
+
+ return res;
+}
+
+
+/**
+ * Compute Digital gains for calibration
+ * @param scan select the scan mode which should be enabled
+ * @param enableGains =1 apply gains when computing the strength otherwise
+ * the gains will be ignored
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_set_scan_mode(u8 scan, int enableGains)
+{
+ int res, ret = OK;
+ u8 cmd[4] = { 0xC0, 0x00, 0x01, 0x00 };
+
+
+ pr_info("%s: Set TP Sensitivity Scan Mode... scan = %02X, enableGains = %d\n",
+ __func__, scan, enableGains);
+
+
+ if (enableGains == 1) {
+ /* Consider Sensitivity Gains when computing Strength */
+ cmd[3] = 0x01;
+ ret = fts_writeFwCmd(cmd, 4);
+ if (ret < OK)
+ pr_err("%s: Error while enabling Gains in TP Sens Mode! ERROR %08X\n",
+ __func__, ret);
+ } else {
+ /* Exclude Sensitivity Gains when computing Strength */
+ ret = fts_writeFwCmd(cmd, 4);
+ if (ret < OK)
+ pr_err("%s: Error while disabling Gain in TP Sens Mode! ERROR %08X\n",
+ __func__, ret);
+ }
+
+ res = setScanMode(SCAN_MODE_LOCKED, scan);
+ if (res < OK)
+ pr_err("Error while setting the scan frequency... ERROR %08X\n",
+ res);
+
+ res |= ret;
+
+ if (res < OK)
+ pr_err("%s: Set TP Sensitivity Scan Mode... ERROR %08X!\n",
+ __func__, res);
+ else
+ pr_info("%s: Set TP Sensitivity Scan FINISHED!\n", __func__);
+
+ return res;
+}
+
+
+
+
+/**
+ * Compute the standard deviation for each node form a series of frames
+ * @param numFrames number of frames to collect to compute the standard
+ * deviation
+ * @param[out] std pointer to the frame which will contain the standard
+ * deviation for each node
+ * @return OK if success or an error code which specify the type of error
+ */
+int tp_sensitivity_test_std_ms(int numFrames, MutualSenseFrame *std)
+{
+ int ret = OK;
+ int i = 0, count = 0;
+ MutualSenseFrame frame;
+ int *mean = NULL;/* store the mean value for each node */
+ unsigned long *stdTemp = NULL;
+
+
+ if (std == NULL) {
+ pr_err("%s: Invalid arguments Passed! ERROR %08X\n",
+ __func__, ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+
+ std->node_data = NULL;
+
+ pr_info("%s: Start TP sensitivity STD... collecting %d frames!\n",
+ __func__, numFrames);
+
+ /* collect frames skipping the tests + print on the log */
+ ret = getMSFrame3(MS_STRENGTH, &frame);
+ if (ret < OK) {
+ pr_err("%s: can not read MS Frame... ERROR %08X\n",
+ __func__, ret);
+ goto ERROR;
+ }
+
+ std->header = frame.header;
+ std->node_data_size = frame.node_data_size;
+
+ std->node_data = (short *)kzalloc(std->node_data_size * sizeof(short),
+ GFP_KERNEL);
+ mean = (int *)kzalloc(std->node_data_size * sizeof(int), GFP_KERNEL);
+ stdTemp = (unsigned long *)kzalloc(std->node_data_size *
+ sizeof(unsigned long),
+ GFP_KERNEL);
+ if (std->node_data == NULL ||
+ mean == NULL ||
+ stdTemp == NULL) {
+ pr_err("%s: can not allocate memory ERROR %08X\n",
+ __func__, ERROR_ALLOC | ERROR_GET_FRAME);
+ ret = ERROR_ALLOC | ERROR_GET_FRAME;
+ goto ERROR;
+ }
+
+ /* collecting frames */
+ do {
+ for (i = 0; i < frame.node_data_size; i++) {
+ mean[i] += frame.node_data[i];
+ stdTemp[i] += frame.node_data[i] * frame.node_data[i];
+ }
+ count++;
+
+ if (frame.node_data != NULL) {
+ kfree(frame.node_data);
+ frame.node_data = NULL;
+ }
+
+ /* exclude one more reading at the end*/
+ if (count < numFrames)
+ ret = getMSFrame3(MS_STRENGTH, &frame);
+ } while ((count < numFrames) && (ret >= OK));
+
+ if (ret < OK) {
+ pr_err("%s: error while collecting the frames! ERROR%08X\n",
+ __func__, ret);
+ goto ERROR;
+ }
+
+ /* compute the average for each node */
+ pr_info("%s: Computing std for each node...\n", __func__);
+
+ for (i = 0; i < std->node_data_size; i++) {
+ mean[i] /= numFrames;
+ stdTemp[i] = stdTemp[i] / numFrames - (mean[i] * mean[i]);
+ std->node_data[i] = (short)int_sqrt(stdTemp[i]);
+ }
+
+ kfree(stdTemp);
+ stdTemp = NULL;
+ kfree(mean);
+ mean = NULL;
+
+ /* print average frame in the log */
+ print_frame_short("STD =",
+ array1dTo2d_short(
+ std->node_data,
+ std->node_data_size,
+ std->header.sense_node),
+ std->header.force_node,
+ std->header.sense_node);
+
+ if (ret < OK)
+ pr_err("%s: TP sensitivity STD test FAILED... ERROR %08X\n",
+ __func__, ret);
+ else {
+ pr_info("%s: TP sensitivity STD FINISHED!\n",
+ __func__);
+ ret = OK;
+ }
+
+ return ret;
+
+ERROR:
+
+ kfree(frame.node_data);
+ frame.node_data = NULL;
+
+ kfree(std->node_data);
+ std->node_data = NULL;
+
+ kfree(stdTemp);
+ stdTemp = NULL;
+
+ kfree(mean);
+ mean = NULL;
+
+ return ret;
+}
+
+
+/**
+ * Retrieve the actual Test Limit data from the system (bin file or header
+ * file)
+ * @param path name of Production Test Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param file pointer to the LimitFile struct which will contains the limits
+ * data
+ * @return OK if success or an error code which specify the type of error
+ */
+int getLimitsFile(const char *path, LimitFile *file)
+{
+ const struct firmware *fw = NULL;
+ struct device *dev = NULL;
+ int fd = -1;
+
+ pr_info("Get Limits File starting... %s\n", path);
+
+ if (file->data != NULL) {
+ /* to avoid memory leak on consecutive call of
+ * the function with the same pointer */
+ pr_err("Pointer to Limits Data already contains something... freeing its content!\n");
+ kfree(file->data);
+ file->data = NULL;
+ file->size = 0;
+ }
+
+ strlcpy(file->name, path, MAX_LIMIT_FILE_NAME);
+ if (strncmp(path, "NULL", 4) == 0) {
+#ifdef LIMITS_H_FILE
+ pr_info("Loading Limits File from .h!\n");
+ file->size = LIMITS_SIZE_NAME;
+ file->data = (char *)kmalloc((file->size) * sizeof(char),
+ GFP_KERNEL);
+ if (file->data != NULL) {
+ memcpy(file->data, (char *)(LIMITS_ARRAY_NAME),
+ file->size);
+ return OK;
+ } else {
+ pr_err("Error while allocating data... ERROR %08X\n",
+ path, ERROR_ALLOC);
+ return ERROR_ALLOC;
+ }
+#else
+ pr_err("limit file path NULL... ERROR %08X\n",
+ ERROR_FILE_NOT_FOUND);
+ return ERROR_FILE_NOT_FOUND;
+#endif
+ } else {
+ dev = getDev();
+ if (dev != NULL) {
+ pr_info("Loading Limits File from .csv!\n");
+ fd = request_firmware(&fw, path, dev);
+ if (fd == 0) {
+ pr_info("Start to copy %s...\n", path);
+ file->size = fw->size;
+ file->data = (char *)kmalloc((file->size) *
+ sizeof(char),
+ GFP_KERNEL);
+ if (file->data != NULL) {
+ memcpy(file->data, (char *)fw->data,
+ file->size);
+ pr_info("Limit file Size = %d\n",
+ file->size);
+ release_firmware(fw);
+ return OK;
+ } else {
+ pr_err("Error while allocating data... ERROR %08X\n",
+ ERROR_ALLOC);
+ release_firmware(fw);
+ return ERROR_ALLOC;
+ }
+ } else {
+ pr_err("Request the file %s failed... ERROR %08X\n",
+ path, ERROR_FILE_NOT_FOUND);
+ return ERROR_FILE_NOT_FOUND;
+ }
+ } else {
+ pr_err("Error while getting the device ERROR %08X\n",
+ ERROR_FILE_READ);
+ return ERROR_FILE_READ;
+ }
+ }
+}
+
+/**
+ * Reset and release the memory which store a Production Limit File previously
+ * loaded
+ * @param file pointer to the LimitFile struct to free
+ * @return OK if success or an error code which specify the type of error
+ */
+
+int freeLimitsFile(LimitFile *file)
+{
+ pr_info("Freeing Limit File ...\n");
+ if (file != NULL) {
+ if (file->data != NULL) {
+ kfree(file->data);
+ file->data = NULL;
+ } else
+ pr_err("Limit File was already freed!\n");
+ file->size = 0;
+ strlcpy(file->name, " ", MAX_LIMIT_FILE_NAME);
+ return OK;
+ } else {
+ pr_err("Passed a NULL argument! ERROR %08X\n",
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
+
+/**
+ * Reset and release the memory which store the current Limit File
+ * previously loaded
+ * @return OK if success or an error code which specify the type of error
+ */
+int freeCurrentLimitsFile(void)
+{
+ return freeLimitsFile(&limit_file);
+}
+
+/**
+ * Parse the raw data read from a Production test limit file in order
+ * to find the specified information
+ * If no limits file data are passed, the function loads and stores the limit
+ * file from the system
+ * @param path name of Production Test Limit file to load or
+ * "NULL" if the limits data should be loaded by a .h file
+ * @param file pointer to LimitFile struct that should be parsed or
+ * NULL if the limit file in the system should be loaded and then parsed
+ * @param label string which identify a particular set of data in the file that
+ * want to be loaded
+ * @param data pointer to the pointer which will contains the specified limits
+ * data
+ * as 1 dimension matrix with data arranged row after row
+ * @param row pointer to a int variable which will contain the number of row of
+ * data
+ * @param column pointer to a int variable which will contain the number of
+ * column of data
+ * @return OK if success or an error code which specify the type of error
+ */
+int parseProductionTestLimits(const char *path, LimitFile *file, 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];
+ char *buf = NULL;
+ int n, size, pointer = 0, ret = OK;
+ char *data_file = NULL;
+
+ if (file == NULL || strcmp(path, file->name) != 0 || file->size == 0) {
+ struct fts_ts_info *info = dev_get_drvdata(getDev());
+ const char *limits_file = info->board->limits_name;
+
+ pr_info("No limit File data passed... try to get them from the system!\n");
+ ret = getLimitsFile(limits_file, &limit_file);
+ if (ret < OK) {
+ pr_err("parseProductionTestLimits: ERROR %08X\n",
+ ERROR_FILE_NOT_FOUND);
+ return ERROR_FILE_NOT_FOUND;
+ }
+ size = limit_file.size;
+ data_file = limit_file.data;
+ } else {
+ pr_info("Limit File data passed as arguments!\n");
+ size = file->size;
+ data_file = file->data;
+ }
+
+
+
+ pr_info("The size of the limits file is %d bytes...\n", 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;
+ if (line[0] == '*') {
+ /* each header row start with * ex. *label,n_row,n_colum */
+ line2 = kstrdup(line, GFP_KERNEL);
+ if (line2 == NULL) {
+ pr_err("parseProductionTestLimits: kstrdup ERROR %08X\n",
+ ERROR_ALLOC);
+ ret = ERROR_ALLOC;
+ goto END;
+ }
+ buf = line2;
+ line2 += 1;
+ token = strsep(&line2, ",");
+ if (strcmp(token, label) == 0) {
+ /* if the row is the wanted one, r
+ * retrieve rows and columns info */
+ find = 1;
+ token = strsep(&line2, ",");
+ if (token != NULL) {
+ sscanf(token, "%d", row);
+ pr_info("Row = %d\n", *row);
+ } else {
+ pr_err("parseProductionTestLimits 1: ERROR %08X\n",
+ ERROR_FILE_PARSE);
+ ret = ERROR_FILE_PARSE;
+ goto END;
+ }
+ token = strsep(&line2, ",");
+ if (token != NULL) {
+ sscanf(token, "%d", column);
+ pr_info("Column = %d\n", *column);
+ } else {
+ pr_err("parseProductionTestLimits 2: ERROR %08X\n",
+ ERROR_FILE_PARSE);
+ ret = ERROR_FILE_PARSE;
+ goto END;
+ }
+
+ kfree(buf);
+ buf = NULL;
+ *data = (int *)kmalloc(((*row) * (*column)) *
+ sizeof(int), GFP_KERNEL);
+ /* allocate the memory for containing the data */
+ j = 0;
+ if (*data == NULL) {
+ pr_err("parseProductionTestLimits: ERROR %08X\n",
+ ERROR_ALLOC);
+ ret = ERROR_ALLOC;
+ goto END;
+ }
+
+
+ /* start to read the data */
+ for (i = 0; i < *row; i++) {
+ if (readLine(&data_file[pointer], line,
+ size - pointer, &n) < 0) {
+ pr_err("parseProductionTestLimits : ERROR %08X\n",
+ ERROR_FILE_READ);
+ ret = ERROR_FILE_READ;
+ goto END;
+ }
+ pointer += n;
+ line2 = kstrdup(line, GFP_KERNEL);
+ if (line2 == NULL) {
+ pr_err("parseProductionTestLimits: kstrdup ERROR %08X\n",
+ ERROR_ALLOC);
+ ret = ERROR_ALLOC;
+ goto END;
+ }
+ buf = line2;
+ token = strsep(&line2, ",");
+ for (z = 0; (z < *column) && (token !=
+ NULL);
+ z++) {
+ sscanf(token, "%d", ((*data) +
+ j));
+ j++;
+ token = strsep(&line2, ",");
+ }
+ kfree(buf);
+ buf = NULL;
+ }
+ if (j == ((*row) * (*column))) {
+ /* check that all the data are read */
+ pr_info("READ DONE!\n");
+ ret = OK;
+ goto END;
+ }
+ pr_err("parseProductionTestLimits 3: ERROR %08X\n",
+ ERROR_FILE_PARSE);
+ ret = ERROR_FILE_PARSE;
+ goto END;
+ }
+ kfree(buf);
+ buf = NULL;
+ }
+ }
+ pr_err("parseProductionTestLimits: ERROR %08X\n",
+ ERROR_LABEL_NOT_FOUND);
+ ret = ERROR_LABEL_NOT_FOUND;
+END:
+ if (buf != NULL)
+ kfree(buf);
+ return ret;
+}
+
+
+/**
+ * Read one line of a text file passed as array of byte and terminate it with
+ * a termination character '\0'
+ * @param data text file as array of bytes
+ * @param line pointer to an array of char that will contain the line read
+ * @param size size of data
+ * @param n pointer to a int variable which will contain the number of
+ * characters of the line
+ * @return OK if success or an error code which specify the type of error
+ */
+int readLine(char *data, char *line, int size, int *n)
+{
+ int i = 0;
+
+ if (size < 1)
+ return ERROR_OP_NOT_ALLOW;
+
+ while (data[i] != '\n' && i < size) {
+ line[i] = data[i];
+ i++;
+ }
+ *n = i + 1;
+ line[i] = '\0';
+
+ return OK;
+}
diff --git a/fts_lib/ftsTest.h b/fts_lib/ftsTest.h
new file mode 100644
index 0000000..bb899ad
--- /dev/null
+++ b/fts_lib/ftsTest.h
@@ -0,0 +1,500 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS API for MP test **
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTest.h
+ * \brief Contains all the definitions and structs related to the Mass
+ *Production Test
+ */
+
+#ifndef FTS_TEST_H
+#define FTS_TEST_H
+
+#include "ftsSoftware.h"
+#include "ftsFrame.h"
+
+#ifndef LIMITS_H_FILE
+/* /< Name of the Production Test Limit File */
+#define LIMITS_FILE "stm_fts_production_limits.csv"
+#else
+#define LIMITS_FILE "NULL"
+#endif
+
+#define WAIT_FOR_FRESH_FRAMES 200 /* /< Time in ms to wait after
+ * start to sensing before
+ * reading a frame */
+#define WAIT_AFTER_SENSEOFF 50 /* /< Time in ms to wait after
+ * stop sensing and before
+ * reading a frame from
+ * memory */
+
+#define NO_INIT 0 /* /< No Initialization required
+ * during the MP */
+
+#define RETRY_INIT_BOOT 3 /* /< number of retry of the
+ * init process at boot */
+
+#define SENS_TEST_NUM_FRAMES 100 /* /< number of frames to read */
+#define SENS_TEST_PERC_TARGET_PRECAL 20 /* /< +-% of target value within
+ * all the node of the frames
+ * should be contained */
+#define SENS_TEST_PERC_TARGET_POSTCAL 4 /* /< +-% of target value within
+ * should be contained the
+ * avarege of the nodes and the
+ * deltas for each node */
+
+/** @defgroup mp_test Mass Production Test
+ * Mass production test API.
+ * Mass Production Test (MP) should be executed at least one time in the life
+ * of every device \n
+ * It used to verify that tit is not present any hardware damage and
+ * initialize some value of the chip in order to guarantee the working
+ * performance \n
+ * The MP test is made up by 3 steps:
+ * - ITO test = production_test_ito() \n
+ * - Initialization = production_test_initialization() \n
+ * - Data Test = production_test_data(),
+ * it is possible to select which items test thanks to the TestToDo struct\n
+ * To execute the Data Test it is mandatory load some thresholds that
+ * are stored in the Limit File.
+ * @{
+ */
+
+/** @defgroup limit_file Limit File
+ * @ingroup mp_test
+ * Production Test Limit File is a csv which contains thresholds of the data to
+ * test.
+ * This file can be loaded from the file system or stored as a header file
+ * according to the LIMITS_H_FILE define \n
+ * For each selectable test item there can be one or more associated labels
+ * which store the corresponding thresholds \n
+ * @{
+ */
+/* LABELS PRODUCTION TEST LIMITS FILE */
+/** @defgroup test_labels Test Items Labels
+ * @ingroup limit_file
+ * Labels present in the Limit File and associated to the test items of
+ * TestToDo
+ * @{
+ */
+#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX"
+#define MS_RAW_EACH_NODE_MIN "MS_RAW_DATA_EACH_MIN"
+#define MS_RAW_EACH_NODE_MAX "MS_RAW_DATA_EACH_MAX"
+#define MS_RAW_GAP "MS_RAW_DATA_GAP"
+#define MS_RAW_ADJH "MS_RAW_DATA_ADJ_HORIZONTAL"
+#define MS_RAW_ADJV "MS_RAW_DATA_ADJ_VERTICAL"
+#define MS_RAW_ITO_ADJH "MS_RAW_ITO_DATA_ADJ_HORIZONTAL"
+#define MS_RAW_ITO_ADJV "MS_RAW_ITO_DATA_ADJ_VERTICAL"
+#define MS_RAW_ITO_MIN_MAX "MS_RAW_ITO_MIN_MAX"
+#define MS_RAW_ITO_EACH_NODE_MIN "MS_RAW_ITO_DATA_MIN"
+#define MS_RAW_ITO_EACH_NODE_MAX "MS_RAW_ITO_DATA_MAX"
+#define MS_RAW_LP_MIN_MAX "MS_RAW_LOWPOWER_DATA_MIN_MAX"
+#define MS_RAW_LP_EACH_NODE_MIN "MS_RAW_LOWPOWER_DATA_EACH_MIN"
+#define MS_RAW_LP_EACH_NODE_MAX "MS_RAW_LOWPOWER_DATA_EACH_MAX"
+#define MS_RAW_LP_GAP "MS_RAW_LOWPOWER_DATA_GAP"
+#define MS_RAW_LP_ADJH "MS_RAW_LOWPOWER_DATA_ADJ_HORIZONTAL"
+#define MS_RAW_LP_ADJV "MS_RAW_LOWPOWER_DATA_ADJ_VERTICAL"
+#define MS_RAW_ADJH_GAP "MS_RAW_DATA_ADJ_HORIZONTAL_P2P"
+#define MS_RAW_ADJV_GAP "MS_RAW_DATA_ADJ_VERTICAL_P2P"
+#define MS_RAW_ADJ_PEAK "MS_RAW_DATA_ADJ_PEAK"
+#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 MS_CX1_LP_MIN_MAX "MS_TOUCH_LOWPOWER_CX1_MIN_MAX"
+#define MS_CX2_LP_MAP_MIN "MS_TOUCH_LOWPOWER_CX2_MIN"
+#define MS_CX2_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_MAX"
+#define MS_CX2_ADJH_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_ADJ_HORIZONTAL"
+#define MS_CX2_ADJV_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_ADJ_VERTICAL"
+#define MS_TOTAL_CX_LP_MAP_MIN "MS_TOUCH_LOWPOWER_TOTAL_CX_MIN"
+#define MS_TOTAL_CX_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_MAX"
+#define MS_TOTAL_CX_ADJH_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_ADJ_HORIZONTAL"
+#define MS_TOTAL_CX_ADJV_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_ADJ_VERTICAL"
+#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX"
+#define SS_RAW_FORCE_EACH_NODE_MIN "SS_RAW_DATA_FORCE_EACH_MIN"
+#define SS_RAW_FORCE_EACH_NODE_MAX "SS_RAW_DATA_FORCE_EACH_MAX"
+#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX"
+#define SS_RAW_SENSE_EACH_NODE_MIN "SS_RAW_DATA_SENSE_EACH_MIN"
+#define SS_RAW_SENSE_EACH_NODE_MAX "SS_RAW_DATA_SENSE_EACH_MAX"
+#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP"
+#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP"
+#define SS_RAW_LP_FORCE_MIN_MAX "SS_RAW_LOWPOWER_DATA_FORCE_MIN_MAX"
+#define SS_RAW_LP_SENSE_MIN_MAX "SS_RAW_LOWPOWER_DATA_SENSE_MIN_MAX"
+#define SS_RAW_LP_FORCE_EACH_NODE_MIN "SS_RAW_LOWPOWER_DATA_FORCE_EACH_MIN"
+#define SS_RAW_LP_FORCE_EACH_NODE_MAX "SS_RAW_LOWPOWER_DATA_FORCE_EACH_MAX"
+#define SS_RAW_LP_SENSE_MIN_MAX "SS_RAW_LOWPOWER_DATA_SENSE_MIN_MAX"
+#define SS_RAW_LP_SENSE_EACH_NODE_MIN "SS_RAW_LOWPOWER_DATA_SENSE_EACH_MIN"
+#define SS_RAW_LP_SENSE_EACH_NODE_MAX "SS_RAW_LOWPOWER_DATA_SENSE_EACH_MAX"
+#define SS_RAW_LP_FORCE_GAP "SS_RAW_LOWPOWER_DATA_FORCE_GAP"
+#define SS_RAW_LP_SENSE_GAP "SS_RAW_LOWPOWER_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"
+
+/* Idle (LP) version*/
+#define SS_IX1_LP_FORCE_MIN_MAX "SS_TOUCH_IDLE_IX1_FORCE_MIN_MAX"
+#define SS_IX1_LP_SENSE_MIN_MAX "SS_TOUCH_IDLE_IX1_SENSE_MIN_MAX"
+#define SS_CX1_LP_FORCE_MIN_MAX "SS_TOUCH_IDLE_CX1_FORCE_MIN_MAX"
+#define SS_CX1_LP_SENSE_MIN_MAX "SS_TOUCH_IDLE_CX1_SENSE_MIN_MAX"
+#define SS_IX2_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_IX2_FORCE_MIN"
+#define SS_IX2_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_IX2_FORCE_MAX"
+#define SS_IX2_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_IX2_SENSE_MIN"
+#define SS_IX2_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_IX2_SENSE_MAX"
+#define SS_IX2_LP_FORCE_ADJV_MAP_MAX "SS_TOUCH_IDLE_IX2_ADJ_VERTICAL"
+#define SS_IX2_LP_SENSE_ADJH_MAP_MAX "SS_TOUCH_IDLE_IX2_ADJ_HORIZONTAL"
+#define SS_CX2_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_CX2_FORCE_MIN"
+#define SS_CX2_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_CX2_FORCE_MAX"
+#define SS_CX2_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_CX2_SENSE_MIN"
+#define SS_CX2_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_CX2_SENSE_MAX"
+#define SS_CX2_LP_FORCE_ADJV_MAP_MAX "SS_TOUCH_IDLE_CX2_ADJ_VERTICAL"
+#define SS_CX2_LP_SENSE_ADJH_MAP_MAX "SS_TOUCH_IDLE_CX2_ADJ_HORIZONTAL"
+
+
+/* TOTAL SS */
+#define SS_TOTAL_IX_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_IX_FORCE_MIN"
+#define SS_TOTAL_IX_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_IX_FORCE_MAX"
+#define SS_TOTAL_IX_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_IX_SENSE_MIN"
+#define SS_TOTAL_IX_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_IX_SENSE_MAX"
+#define SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX \
+ "SS_TOUCH_IDLE_TOTAL_IX_ADJ_VERTICAL"
+#define SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX \
+ "SS_TOUCH_IDLE_TOTAL_IX_ADJ_HORIZONTAL"
+#define SS_TOTAL_CX_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_CX_FORCE_MIN"
+#define SS_TOTAL_CX_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_CX_FORCE_MAX"
+#define SS_TOTAL_CX_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_CX_SENSE_MIN"
+#define SS_TOTAL_CX_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_CX_SENSE_MAX"
+#define SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX \
+ "SS_TOUCH_IDLE_TOTAL_CX_ADJ_VERTICAL"
+#define SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX \
+ "SS_TOUCH_IDLE_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"
+/** @}*/
+
+
+/**
+ * Struct used to specify which test perform during the Mass Production Test.
+ * For each test item selected in this structure, there should be one or
+ * more labels associated in the Limit file from where load the thresholds
+ */
+typedef struct {
+ int MutualRaw; /* /< MS Raw min/Max test */
+ int MutualRawMap; /* /< MS Raw min/Max test for each node */
+ int MutualRawGap; /* /< MS Raw Gap(max-min) test */
+ int MutualRawAdj; /* /< MS Raw Adjacent test */
+ int MutualRawAdjGap; /* /< MS Raw Adjacent Gap (max-min) test */
+ int MutualRawAdjPeak; /* /< MS Raw Adjacent Peak
+ * max(max(adjv),max(adjh)) test
+ */
+ int MutualRawLP; /* /< MS Low Power Raw min/Max test */
+ int MutualRawMapLP; /* /< MS Low Power Raw min/Max test
+ * for each node
+ */
+ int MutualRawGapLP; /* /< MS Low Power Raw Gap(max-min) test */
+ int MutualRawAdjLP; /* /< MS Low Power Raw Adjacent test */
+ int MutualRawAdjITO; /* /< MS Raw Adjacent test during ITO test */
+ int MutualRawMapITO; /* /< MS Raw ITO min/Max test */
+
+ int MutualCx1; /* /< MS Cx1 min/Max test */
+ int MutualCx2; /* /< MS Cx2 min/Max (for each node) test */
+ int MutualCx2Adj; /* /< MS Vertical and Horizontal Adj Cx2 min/Max
+ * (for each node) test */
+ int MutualCxTotal; /* /< MS Total Cx min/Max (for each node) test
+ * */
+ int MutualCxTotalAdj; /* /< MS Total vertical and Horizontal Adj Cx2
+ * min/Max (for each node) test
+ */
+
+ int MutualCx1LP; /* /< MS LowPower Cx1 min/Max test */
+ int MutualCx2LP; /* /< MS LowPower Cx2 min/Max (for each node)
+ * test
+ */
+ int MutualCx2AdjLP; /* /< MS LowPower Vertical and Horizontal Adj
+ * Cx2 min/Max
+ * (for each node) test
+ */
+ int MutualCxTotalLP; /* /< MS Total LowPower Cx min/Max
+ * (for each node) test */
+ int MutualCxTotalAdjLP; /* /< MS Total LowPower vertical and Horizontal
+ * Adj Cx2 min/Max (for each node) test
+ */
+
+ int MutualKeyRaw; /* /< MS Raw Key min/Max test */
+ int MutualKeyCx1; /* /< MS Cx1 Key min/Max test */
+ int MutualKeyCx2; /* /< MS Cx2 Key min/Max (for each node) test */
+ int MutualKeyCxTotal; /* /< MS Total Cx Key min/Max (for each node)
+ * test */
+
+ int SelfForceRaw; /* /< SS Force Raw min/Max test */
+ int SelfForceRawGap; /* /< SS Force Raw Gap(max-min) test */
+ int SelfForceRawMap; /* /< SS Force Raw min/Max Map test */
+ int SelfForceRawLP; /* /< SS Low Power Force Raw min/Max test */
+ int SelfForceRawGapLP; /* /< SS Low Power Force Raw Gap(max-min) test */
+ int SelfForceRawMapLP; /* /< SS Low Power Force Raw min/Max Map test */
+
+ int SelfForceIx1; /* /< SS Force Ix1 min/Max test */
+ int SelfForceIx2; /* /< SS Force Ix2 min/Max (for each node) test
+ * */
+ int SelfForceIx2Adj; /* /< SS Vertical Adj Force Ix2 min/Max
+ * (for each node) test */
+ int SelfForceIxTotal; /* /< SS Total Force Ix min/Max (for each node)
+ * test */
+ int SelfForceIxTotalAdj; /* /< SS Total Vertical Adj Force Ix
+ * min/Max
+ * (for each node) test */
+ int SelfForceCx1; /* /< SS Force Cx1 min/Max test */
+ int SelfForceCx2; /* /< SS Force Cx2 min/Max (for each node) test */
+ int SelfForceCx2Adj; /* /< SS Vertical Adj Force Cx2 min/Max (for
+ * each node) test */
+ int SelfForceCxTotal; /* /< SS Total Force Cx min/Max (for each node)
+ * test */
+ int SelfForceCxTotalAdj; /* /< SS Total Vertical Adj Force Cx
+ * min/Max (for each node) test
+ */
+
+ int SelfForceIx1LP; /* /< SS LP Force Ix1 min/Max test */
+ int SelfForceIx2LP; /* /< SS LP Force Ix2 min/Max (for each node)
+ * test
+ */
+ int SelfForceIx2AdjLP; /* /< SS LP Vertical Adj Force Ix2 min/Max
+ * (for each node) test */
+ int SelfForceIxTotalLP; /* /< SS LP Total Force Ix min/Max
+ * (for each node) test
+ */
+ int SelfForceIxTotalAdjLP; /* /< SS LP Total Vertical Adj Force Ix
+ * min/Max (for each node) test
+ */
+ int SelfForceCx1LP; /* /< SS LP Force Cx1 min/Max test */
+ int SelfForceCx2LP; /* /< SS LP Force Cx2 min/Max (for each node)
+ * test
+ */
+ int SelfForceCx2AdjLP; /* /< SS LP Vertical Adj Force Cx2 min/Max (for
+ * each node) test
+ */
+ int SelfForceCxTotalLP; /* /< SS LP Total Force Cx min/Max
+ * (for each node) test
+ */
+ int SelfForceCxTotalAdjLP; /* /< SS LP Total Vertical Adj Force Cx
+ * min/Max (for each node) test
+ */
+
+ int SelfSenseRaw; /* /< SS Sense Raw min/Max test */
+ int SelfSenseRawGap; /* /< SS Sense Raw Gap(max-min) test */
+ int SelfSenseRawMap; /* /< SS Sense Raw min/Max test for each node */
+ int SelfSenseRawLP; /* /< SS Low Power Sense Raw min/Max test */
+ int SelfSenseRawGapLP; /* /< SS Low Power Sense Raw Gap(max-min) test */
+ int SelfSenseRawMapLP; /* /< SS Low Power Sense Raw min/Max test for
+ * each node
+ */
+
+ int SelfSenseIx1; /* /< SS Sense Ix1 min/Max test */
+ int SelfSenseIx2; /* /< SS Sense Ix2 min/Max (for each node) test */
+ int SelfSenseIx2Adj; /* /< SS Horizontal Adj Sense Ix2 min/Max
+ * (for each node) test */
+ int SelfSenseIxTotal; /* /< SS Total Horizontal Sense Ix min/Max
+ * (for each node) test */
+ int SelfSenseIxTotalAdj; /* /< SS Total Horizontal Adj Sense Ix
+ * min/Max
+ * (for each node) test */
+ int SelfSenseCx1; /* /< SS Sense Cx1 min/Max test */
+ int SelfSenseCx2; /* /< SS Sense Cx2 min/Max (for each node) test */
+ int SelfSenseCx2Adj; /* /< SS Horizontal Adj Sense Cx2 min/Max
+ * (for each node) test */
+ int SelfSenseCxTotal; /* /< SS Total Sense Cx min/Max (for each node)
+ * test */
+ int SelfSenseCxTotalAdj; /* /< SS Total Horizontal Adj Sense Cx
+ * min/Max
+ * (for each node) test */
+ int SelfSenseIx1LP; /* /< SS LP Sense Ix1 min/Max test */
+ int SelfSenseIx2LP; /* /< SS LP Sense Ix2 min/Max (for each node)
+ * test
+ */
+ int SelfSenseIx2AdjLP; /* /< SS LP Horizontal Adj Sense Ix2 min/Max
+ * (for each node) test
+ */
+ int SelfSenseIxTotalLP; /* /< SS LP Total Horizontal Sense Ix min/Max
+ * (for each node) test
+ */
+ int SelfSenseIxTotalAdjLP; /* /< SS LP Total Horizontal Adj Sense Ix
+ * min/Max (for each node) test
+ */
+ int SelfSenseCx1LP; /* /< SS LP Sense Cx1 min/Max test */
+ int SelfSenseCx2LP; /* /< SS LP Sense Cx2 min/Max (for each node)
+ * test
+ */
+ int SelfSenseCx2AdjLP; /* /< SS LP Horizontal Adj Sense Cx2 min/Max
+ * (for each node) test
+ */
+ int SelfSenseCxTotalLP; /* /< SS LP Total Sense Cx min/Max
+ * (for each node) test
+ */
+ int SelfSenseCxTotalAdjLP; /* /< SS LP Total Horizontal Adj Sense Cx
+ * min/Max (for each node) test
+ */
+} TestToDo;
+
+
+#define MAX_LIMIT_FILE_NAME 100 /* /< max number of chars of the limit file name
+ * */
+
+/**
+ * Struct which store the data coming from a Production Limit File
+ */
+typedef struct {
+ char *data; /* /< pointer to an array of char which contains
+ * the content of the Production Limit File */
+ int size; /* /< size of data */
+ char name[MAX_LIMIT_FILE_NAME]; /* /< identifier of the source from
+ * where the limits data were loaded
+ * (if loaded from a file it will be
+ * the file name, while if loaded
+ * from .h will be "NULL") */
+} LimitFile;
+
+
+
+int initTestToDo(void);
+/**@}*/
+
+/**@}*/
+
+
+int computeAdjHoriz(i8 *data, int row, int column, u8 **result);
+int computeAdjHorizTotal(short *data, int row, int column, u16 **result);
+int computeAdjVert(i8 *data, int row, int column, u8 **result);
+int computeAdjVertTotal(short *data, int row, int column, u16 **result);
+int computeAdjHorizFromU(u8 *data, int row, int column, u8 **result);
+int computeAdjHorizTotalFromU(u16 *data, int row, int column, u16 **result);
+int computeAdjVertFromU(u8 *data, int row, int column, u8 **result);
+int computeAdjVertTotalFromU(u16 *data, int row, int column, u16 **result);
+int checkLimitsMinMax(short *data, int row, int column, int min, int max);
+int checkLimitsMap(i8 *data, int row, int column, int *min, int *max);
+int checkLimitsMapTotal(short *data, int row, int column, int *min, int *max);
+int checkLimitsMapFromU(u8 *data, int row, int column, int *min, int *max);
+int checkLimitsMapTotalFromU(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 checkLimitsGap(short *data, int row, int column, int threshold);
+int checkLimitsGapOffsets(short *data, int row, int column, int threshold,
+ int row_start, int column_start, int row_end, int column_end);
+
+/** @defgroup mp_api MP API
+ * @ingroup mp_test
+ * Functions to execute the MP test.
+ * The parameters of these functions allow to customize their behavior
+ * in order to satisfy different scenarios
+ * @{
+ */
+int production_test_ito(const char *path_limits, TestToDo *todo,
+ MutualSenseFrame *frame);
+int production_test_initialization(u8 type);
+int production_test_main(const char *pathThresholds, int stop_on_fail,
+ int saveInit, TestToDo *todo, u8 mpflag);
+int production_test_ms_raw(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ms_raw_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ms_cx(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ms_cx_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ss_raw(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ss_raw_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ss_ix_cx(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ss_ix_cx_lp(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_data(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ms_key_cx(const char *path_limits, int stop_on_fail,
+ TestToDo *todo);
+int production_test_ms_key_raw(const char *path_limits);
+/** @}*/
+
+/**
+ * @addtogroup limit_file
+ * @{
+ */
+int parseProductionTestLimits(const char *path, LimitFile *file, char *label,
+ int **data, int *row, int *column);
+int readLine(char *data, char *line, int size, int *n);
+int getLimitsFile(const char *path, LimitFile *file);
+int freeLimitsFile(LimitFile *file);
+int freeCurrentLimitsFile(void);
+/**@}*/
+
+int tp_sensitivity_test_pre_cal_ms(MutualSenseFrame *finalFrame, short target,
+ int percentage);
+int tp_sensitivity_test_pre_cal_ss(SelfSenseFrame *finalFrame, short target,
+ int percentage);
+int tp_sensitivity_compute_gains(MutualSenseFrame *frame, short target,
+ int saveGain);
+int tp_sensitivity_test_post_cal_ms(MutualSenseFrame *finalFrame,
+ MutualSenseFrame *deltas, short target,
+ int percentage, int *mean_normal,
+ int *mean_edge);
+int tp_sensitivity_set_scan_mode(u8 scan, int enableGains);
+int tp_sensitivity_mode(u8 enter, int saveGain);
+int tp_sensitivity_test_std_ms(int numFrames, MutualSenseFrame *std);
+#endif
diff --git a/fts_lib/ftsTime.c b/fts_lib/ftsTime.c
new file mode 100644
index 0000000..9ab18fd
--- /dev/null
+++ b/fts_lib/ftsTime.c
@@ -0,0 +1,84 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Utility for mesuring/handling the time *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTime.c
+ * \brief Contains all functions to handle and measure the time in the driver
+ */
+
+#include "ftsTime.h"
+
+
+#include <linux/errno.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+
+/**
+ * Take the starting time and save it in a StopWatch variable
+ * @param w pointer of a StopWatch struct
+ */
+void startStopWatch(StopWatch *w)
+{
+ w->start = current_kernel_time();
+}
+
+/**
+ * Take the stop time and save it in a StopWatch variable
+ * @param w pointer of a StopWatch struct
+ */
+void stopStopWatch(StopWatch *w)
+{
+ w->end = current_kernel_time();
+}
+
+/**
+ * Compute the amount of time spent from when the startStopWatch and then
+ * the stopStopWatch were called on the StopWatch variable
+ * @param w pointer of a StopWatch struct
+ * @return amount of time in ms (the return value is meaningless
+ * if the startStopWatch and stopStopWatch were not called before)
+ */
+int elapsedMillisecond(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;
+}
+
+/**
+ * Compute the amount of time spent from when the startStopWatch and
+ * then the stopStopWatch were called on the StopWatch variable
+ * @param w pointer of a StopWatch struct
+ * @return amount of time in ns (the return value is meaningless
+ * if the startStopWatch and stopStopWatch were not called before)
+ */
+int elapsedNanosecond(StopWatch *w)
+{
+ int result;
+
+ result = ((w->end.tv_sec - w->start.tv_sec) * 1000000000) +
+ (w->end.tv_nsec - w->start.tv_nsec);
+ return result;
+}
diff --git a/fts_lib/ftsTime.h b/fts_lib/ftsTime.h
new file mode 100644
index 0000000..ffdf66a
--- /dev/null
+++ b/fts_lib/ftsTime.h
@@ -0,0 +1,74 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Utility for measuring/handling the time *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTime.h
+ * \brief Contains all the definitions and structs to handle and measure the
+ * time in the driver
+ */
+
+#ifndef FTS_TIME_H
+#define FTS_TIME_H
+
+
+#include <linux/time.h>
+
+/* TIMEOUT */
+/** @defgroup timeouts Timeouts
+ * Definitions of all the Timeout used in several operations
+ * @{
+ */
+#define TIMEOUT_RESOLUTION 50
+/* /< timeout resolution in ms (all timeout should be multiples of this unit) */
+#define GENERAL_TIMEOUT (15 * TIMEOUT_RESOLUTION)
+/* /< general timeout in ms */
+#define RELEASE_INFO_TIMEOUT (2 * TIMEOUT_RESOLUTION)
+/* /< timeout to request release info in ms */
+
+
+#define TIMEOUT_REQU_COMP_DATA (4 * TIMEOUT_RESOLUTION)
+/* /< timeout to request compensation data in ms */
+#define TIMEOUT_REQU_DATA (8 * TIMEOUT_RESOLUTION)
+/* /< timeout to request data in ms */
+#define TIMEOUT_ITO_TEST_RESULT (4 * TIMEOUT_RESOLUTION)
+/* /< timeout to perform ito test in ms */
+#define TIMEOUT_INITIALIZATION_TEST_RESULT (5000 * TIMEOUT_RESOLUTION)
+/* /< timeout to perform initialization test in ms */
+#define TIEMOUT_ECHO (50 * TIMEOUT_RESOLUTION)
+/* /< timeout of the echo command,*/
+#define TIMEOUT_ECHO_FLUSH (TIMEOUT_RESOLUTION)
+/* /< timeout of the flush echo command,*/
+#define TIMEOUT_ECHO_FPI (200 * TIMEOUT_RESOLUTION)
+/* /< timeout of the Full panel Init echo command */
+#define TIMEOUT_ECHO_SINGLE_ENDED_SPECIAL_AUTOTUNE \
+ (100 * TIMEOUT_RESOLUTION)
+/** @}*/
+
+
+/**
+ * Struct used to measure the time elapsed between a starting and ending point.
+ */
+typedef struct {
+ struct timespec start; /* /< store the starting time */
+ struct timespec end; /* /< store the finishing time */
+} StopWatch;
+
+
+void startStopWatch(StopWatch *w);
+void stopStopWatch(StopWatch *w);
+int elapsedMillisecond(StopWatch *w);
+int elapsedNanosecond(StopWatch *w);
+
+#endif
diff --git a/fts_lib/ftsTool.c b/fts_lib/ftsTool.c
new file mode 100644
index 0000000..6a3f7c5
--- /dev/null
+++ b/fts_lib/ftsTool.c
@@ -0,0 +1,798 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Utility Functions *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTool.c
+ * \brief Contains all the functions to support common operation inside the
+ * driver
+ */
+
+#include "ftsCompensation.h"
+#include "ftsCore.h"
+#include "ftsError.h"
+#include "ftsHardware.h"
+#include "ftsIO.h"
+#include "ftsSoftware.h"
+#include "ftsTime.h"
+#include "ftsTool.h"
+#include "../fts.h" /* needed for the tag define */
+
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+
+/**
+ * Print an array of byte in a HEX string and attach at the beginning a label.
+ * The function allocate memory that should be free outside the function itself
+ * @param label string to attach at the beginning
+ * @param buff pointer to the byte array that should be printed as HEX string
+ * @param count size of buff
+ * @param result pointer to the array of characters that compose the HEX final
+ * string
+ * @param size size of result
+ * @return pointer to the array of characters that compose the HEX string,
+ * (same address of result)
+ * @warning result MUST be allocated outside the function and should be
+ * big enough to contain the data converted as HEX!
+ */
+char *printHex(char *label, u8 *buff, int count, u8 *result, int size)
+{
+ int i, offset = 0;
+
+ offset = scnprintf(result + offset, size - offset, "%s", label);
+ for (i = 0; i < count; i++) {
+ offset +=
+ scnprintf(result + offset,
+ size - offset, "%02X ", buff[i]);
+ /* this append automatically a null terminator char */
+ }
+ return result;
+}
+
+/**
+ * Clear the FIFO from any event
+ * @return OK if success or an error code which specify the type of error
+ */
+int flushFIFO(void)
+{
+ int ret;
+ u8 sett = SPECIAL_FIFO_FLUSH;
+
+ ret = writeSysCmd(SYS_CMD_SPECIAL, &sett, 1); /* flush the FIFO */
+ if (ret < OK) {
+ pr_err("flushFIFO: ERROR %08X\n", ret);
+ return ret;
+ }
+
+ pr_info("FIFO flushed!\n");
+ return OK;
+}
+
+
+
+/**
+ * Convert an array of bytes to an array of u16 taking two bytes at time,
+ * src has LSB first.
+ * @param src pointer to the source byte array
+ * @param src_length size of src
+ * @param dst pointer to the destination array.
+ * @return the final size of dst (half of the source) or ERROR_OP_NOT_ALLOW
+ * if the size of src is not multiple of 2.
+ */
+int u8ToU16n(u8 *src, int src_length, u16 *dst)
+{
+ int i, j;
+
+ if (src_length % 2 != 0)
+ return ERROR_OP_NOT_ALLOW;
+ else {
+ j = 0;
+ for (i = 0; i < src_length; i += 2) {
+ dst[j] = ((src[i + 1] & 0x00FF) << 8) +
+ (src[i] & 0x00FF);
+ j++;
+ }
+ }
+
+ return src_length / 2;
+}
+
+/**
+ * Convert an array of 2 bytes to a u16, src has LSB first (little endian).
+ * @param src pointer to the source byte array
+ * @param dst pointer to the destination u16.
+ * @return OK
+ */
+int u8ToU16(u8 *src, u16 *dst)
+{
+ *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF));
+ return OK;
+}
+
+/**
+ * Convert an array of 2 bytes to a u16, src has MSB first (big endian).
+ * @param src pointer to the source byte array
+ * @param dst pointer to the destination u16.
+ * @return OK
+ */
+int u8ToU16_be(u8 *src, u16 *dst)
+{
+ *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF));
+ return OK;
+}
+
+/**
+ * Convert an array of u16 to an array of u8, dst has MSB first (big endian).
+ * @param src pointer to the source array of u16
+ * @param src_length size of src
+ * @param dst pointer to the destination array of u8.
+ * @return size of dst (src size multiply by 2)
+ */
+int u16ToU8n_be(u16 *src, int src_length, u8 *dst)
+{
+ int i, 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;
+}
+
+/**
+ * Convert a u16 to an array of 2 u8, dst has MSB first (big endian).
+ * @param src u16 to convert
+ * @param dst pointer to the destination array of 2 u8.
+ * @return OK
+ */
+int u16ToU8_be(u16 src, u8 *dst)
+{
+ dst[0] = (u8)((src & 0xFF00) >> 8);
+ dst[1] = (u8)(src & 0x00FF);
+ return OK;
+}
+
+
+/**
+ * Convert a u16 to an array of 2 u8, dst has LSB first (little endian).
+ * @param src u16 to convert
+ * @param dst pointer to the destination array of 2 u8.
+ * @return OK
+ */
+int u16ToU8(u16 src, u8 *dst)
+{
+ dst[1] = (u8)((src & 0xFF00) >> 8);
+ dst[0] = (u8)(src & 0x00FF);
+ return OK;
+}
+
+/**
+ * Convert an array of bytes to a u32, src has LSB first (little endian).
+ * @param src array of bytes to convert
+ * @param dst pointer to the destination u32 variable.
+ * @return OK
+ */
+int u8ToU32(u8 *src, u32 *dst)
+{
+ *dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) +
+ ((src[1] & 0xFF) << 8) + (src[0] & 0xFF));
+ return OK;
+}
+
+/**
+ * Convert an array of bytes to a u32, src has MSB first (big endian).
+ * @param src array of bytes to convert
+ * @param dst pointer to the destination u32 variable.
+ * @return OK
+ */
+int u8ToU32_be(u8 *src, u32 *dst)
+{
+ *dst = (u32)(((src[0] & 0xFF) << 24) + ((src[1] & 0xFF) << 16) +
+ ((src[2] & 0xFF) << 8) + (src[3] & 0xFF));
+ return OK;
+}
+
+
+/**
+ * Convert a u32 to an array of 4 bytes, dst has LSB first (little endian).
+ * @param src u32 value to convert
+ * @param dst pointer to the destination array of 4 bytes.
+ * @return OK
+ */
+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 OK;
+}
+
+/**
+ * Convert a u32 to an array of 4 bytes, dst has MSB first (big endian).
+ * @param src u32 value to convert
+ * @param dst pointer to the destination array of 4 bytes.
+ * @return OK
+ */
+int u32ToU8_be(u32 src, u8 *dst)
+{
+ dst[0] = (u8)((src & 0xFF000000) >> 24);
+ dst[1] = (u8)((src & 0x00FF0000) >> 16);
+ dst[2] = (u8)((src & 0x0000FF00) >> 8);
+ dst[3] = (u8)(src & 0x000000FF);
+ return OK;
+}
+
+/**
+ * Execute a function passed as argment and retry it defined number of times if
+ * not successful
+ * @param code pointer to a function which return an int and doesn't have any
+ * parameters
+ * @param wait_before_retry interval of time in ms to wait between one trial
+ * and another one
+ * @param retry_count max number of retry to attemp
+ * @return last return value obtained from the last execution of the code
+ *function
+ */
+int attempt_function(int (*code)(void), unsigned long wait_before_retry, int
+ retry_count)
+{
+ int result;
+ int count = 0;
+
+ do {
+ result = code();
+ count++;
+ mdelay(wait_before_retry);
+ } while (count < retry_count && result < 0);
+
+
+ if (count == retry_count)
+ return result | ERROR_TIMEOUT;
+ else
+ return result;
+}
+
+/**
+ * Enable all the possible sensing mode supported by the FW
+ * @return OK if success or an error code which specify the type of error
+ */
+int senseOn(void)
+{
+ int ret;
+
+ ret = setScanMode(SCAN_MODE_ACTIVE, 0xFF); /* enable all */
+ if (ret < OK) {
+ pr_err("senseOn: ERROR %08X\n", ret);
+ return ret;
+ }
+
+ pr_info("senseOn: SENSE ON\n");
+ return OK;
+}
+
+/**
+ * Disable all the sensing mode
+ * @return OK if success or an error code which specify the type of error
+ */
+int senseOff(void)
+{
+ int ret;
+
+ ret = setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ if (ret < OK) {
+ pr_err("senseOff: ERROR %08X\n", ret);
+ return ret;
+ }
+
+ pr_info("senseOff: SENSE OFF\n");
+ return OK;
+}
+
+
+
+/**
+ * Clean up the IC status executing a system reset and giving
+ * the possibility to re-enabling the sensing
+ * @param enableTouch if 1, re-enable the sensing and the interrupt of the IC
+ * @return OK if success or an error code which specify the type of error
+ */
+int cleanUp(int enableTouch)
+{
+ int res;
+
+ pr_info("cleanUp: system reset...\n");
+ res = fts_system_reset();
+ if (res < OK)
+ return res;
+ if (enableTouch) {
+ pr_info("cleanUp: enabling touches...\n");
+ res = senseOn(); /* already enable everything */
+ if (res < OK)
+ return res;
+ pr_info("cleanUp: enabling interrupts...\n");
+ res = fts_enableInterrupt(true);
+ if (res < OK)
+ return res;
+ }
+ return OK;
+}
+
+/**
+ * Transform an array of short in a matrix of short with a defined number of
+ * columns and the resulting number of rows
+ * @param data array of bytes to convert
+ * @param size size of data
+ * @param columns number of columns that the resulting matrix should have.
+ * @return a reference to a matrix of short where for each row there are
+ * columns elements
+ */
+short **array1dTo2d_short(short *data, int size, int columns)
+{
+ int i;
+ short **matrix = NULL;
+
+ if (size != 0)
+ matrix = (short **)kmalloc_array(((int)(size / columns)),
+ sizeof(short *), GFP_KERNEL);
+
+ if (matrix != NULL) {
+ for (i = 0; i < (int)(size / columns); i++) {
+ matrix[i] = (short *)kmalloc_array(columns,
+ sizeof(short), GFP_KERNEL);
+ if (!matrix[i])
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!matrix[i / columns])
+ break;
+ matrix[i / columns][i % columns] = data[i];
+ }
+ }
+
+ return matrix;
+}
+
+/**
+ * Transform an array of u16 in a matrix of u16 with a defined number of
+ * columns and the resulting number of rows
+ * @param data array of bytes to convert
+ * @param size size of data
+ * @param columns number of columns that the resulting matrix should have.
+ * @return a reference to a matrix of u16 where for each row there are columns
+ * elements
+ */
+u16 **array1dTo2d_u16(u16 *data, int size, int columns)
+{
+ int i;
+ u16 **matrix = NULL;
+
+ if (size != 0)
+ matrix = (u16 **)kmalloc_array(((int)(size / columns)),
+ sizeof(u16 *), GFP_KERNEL);
+
+ if (matrix != NULL) {
+ for (i = 0; i < (int)(size / columns); i++) {
+ matrix[i] = (u16 *)kmalloc_array(columns,
+ sizeof(u16), GFP_KERNEL);
+ if (!matrix[i])
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!matrix[i / columns])
+ break;
+ matrix[i / columns][i % columns] = data[i];
+ }
+ }
+
+ return matrix;
+}
+
+/**
+ * Transform an array of u8 in a matrix of u8 with a defined number of
+ * columns and the resulting number of rows
+ * @param data array of bytes to convert
+ * @param size size of data
+ * @param columns number of columns that the resulting matrix should have.
+ * @return a reference to a matrix of short where for each row there are
+ * columns elements
+ */
+u8 **array1dTo2d_u8(u8 *data, int size, int columns)
+{
+ int i;
+ u8 **matrix = NULL;
+
+ if (size != 0) {
+ matrix = (u8 **)kmalloc_array(((int)(size / columns)),
+ sizeof(u8 *), GFP_KERNEL);
+ }
+
+ if (matrix != NULL) {
+ for (i = 0; i < (int)(size / columns); i++) {
+ matrix[i] = (u8 *)kmalloc_array(columns,
+ sizeof(u8), GFP_KERNEL);
+ if (!matrix[i])
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!matrix[i / columns])
+ break;
+ matrix[i / columns][i % columns] = data[i];
+ }
+ }
+
+ return matrix;
+}
+
+/**
+ * Transform an array of i8 in a matrix of i8 with a defined number of
+ * columns and the resulting number of rows
+ * @param data array of bytes to convert
+ * @param size size of data
+ * @param columns number of columns that the resulting matrix should have.
+ * @return a reference to a matrix of short where for each row there are
+ * columns elements
+ */
+i8 **array1dTo2d_i8(i8 *data, int size, int columns)
+{
+ int i;
+ i8 **matrix = NULL;
+
+ if (size != 0)
+ matrix = (i8 **)kmalloc_array(((int)(size / columns)),
+ sizeof(i8 *), GFP_KERNEL);
+
+ if (matrix != NULL) {
+ for (i = 0; i < (int)(size / columns); i++) {
+ matrix[i] = (i8 *)kmalloc_array(columns,
+ sizeof(i8), GFP_KERNEL);
+ if (!matrix[i])
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!matrix[i / columns])
+ break;
+ matrix[i / columns][i % columns] = data[i];
+ }
+ }
+
+ return matrix;
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of short row x columns
+ * and free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of short which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_short(char *label, short **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (6 + 1) * column + 1; /* -32768 str len: 6 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of u16 row x columns
+ * and free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of u16 which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_u16(char *label, u16 **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (5 + 1) * column + 1; /* 65535 str len: 5 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of u8 row x columns and
+ * free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of u8 which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_u8(char *label, u8 **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (3 + 1) * column + 1; /* 255 str len: 3 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of i8 row x columns and
+ * free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of u8 which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_i8(char *label, i8 **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (4 + 1) * column + 1; /* -128 str len: 4 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of u32 row x columns
+ * and free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of u32 which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_u32(char *label, u32 **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (10 + 1) * column + 1; /* 4294967295 str len: 10 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+/**
+ * Print in the kernel log a label followed by a matrix of int row x columns
+ * and free its memory
+ * @param label pointer to the string to print before the actual matrix
+ * @param matrix reference to the matrix of int which contain the actual data
+ * @param row number of rows on which the matrix should be print
+ * @param column number of columns for each row
+ */
+void print_frame_int(char *label, int **matrix, int row, int column)
+{
+ int i, j;
+ int buff_len, index;
+ char *buff;
+
+ pr_info("%s\n", label);
+
+ if (matrix == NULL)
+ return;
+
+ buff_len = (11 + 1) * column + 1; /* -2147483648 str len: 11 */
+ buff = kzalloc(buff_len, GFP_KERNEL);
+ if (buff == NULL) {
+ pr_err("%s: fail to allocate buffer\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < row; i++) {
+ if (!matrix[i])
+ break;
+ index = 0;
+ for (j = 0; j < column; j++)
+ index += scnprintf(buff + index, buff_len - index,
+ "%d ", matrix[i][j]);
+ pr_info("%s\n", buff);
+ kfree(matrix[i]);
+ }
+ kfree(matrix);
+ kfree(buff);
+}
+
+
+/**
+ * Convert an array of bytes to an u64, src has MSB first (big endian).
+ * @param src array of bytes
+ * @param dest pointer to the destination u64.
+ * @param size size of src (can be <= 8)
+ * @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8
+ */
+int u8ToU64_be(u8 *src, u64 *dest, int size)
+{
+ int i = 0;
+
+ /* u64 temp =0; */
+ if (size > sizeof(u64))
+ return ERROR_OP_NOT_ALLOW;
+
+ *dest = 0;
+ for (i = 0; i < size; i++)
+ *dest |= (u64)(src[i]) << ((size - 1 - i) * 8);
+
+ return OK;
+}
+
+/**
+ * Convert an u64 to an array of bytes, dest has MSB first (big endian).
+ * @param src value of u64
+ * @param dest pointer to the destination array of bytes.
+ * @param size size of src (can be <= 8)
+ * @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8
+ */
+int u64ToU8_be(u64 src, u8 *dest, int size)
+{
+ int i = 0;
+
+ if (size > sizeof(u64))
+ return ERROR_OP_NOT_ALLOW;
+ else
+ for (i = 0; i < size; i++)
+ dest[i] = (u8)((src >> ((size - 1 - i) * 8)) & 0xFF);
+
+ return OK;
+}
+
+
+
+/*********** NEW API *************/
+
+/**
+ * Convert a value of an id in a bitmask with a 1 in the position of the value
+ * of the id
+ * @param id Value of the ID to convert
+ * @param mask pointer to the bitmask that will be updated with the value of id
+ * @param size dimension in bytes of mask
+ * @return OK if success or ERROR_OP_NOT_ALLOW if size of mask is not enough to
+ * contain ID
+ */
+int fromIDtoMask(u8 id, u8 *mask, int size)
+{
+ if (((int)((id) / 8)) < size) {
+ pr_info("%s: ID = %d Index = %d Position = %d !\n",
+ __func__, id, ((int)((id) / 8)), (id % 8));
+ mask[((int)((id) / 8))] |= 0x01 << (id % 8);
+ return OK;
+ } else {
+ pr_err("%s: Bitmask too small! Impossible contain ID = %d %d>=%d! ERROR %08X\n",
+ __func__, id, ((int)((id) / 8)), size,
+ ERROR_OP_NOT_ALLOW);
+ return ERROR_OP_NOT_ALLOW;
+ }
+}
diff --git a/fts_lib/ftsTool.h b/fts_lib/ftsTool.h
new file mode 100644
index 0000000..284ef6c
--- /dev/null
+++ b/fts_lib/ftsTool.h
@@ -0,0 +1,58 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com **
+ **************************************************************************
+ * *
+ * FTS Utility Functions *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file ftsTool.h
+ * \brief Contains all the definitions to support common operations inside the
+ * driver
+ */
+
+#ifndef FTS_TOOL_H
+#define FTS_TOOL_H
+
+char *printHex(char *label, u8 *buff, int count, u8 *result, int size);
+int u8ToU16(u8 *src, u16 *dst);
+int u8ToU16_be(u8 *src, u16 *dst);
+int u8ToU16n(u8 *src, int src_length, u16 *dst);
+int u16ToU8(u16 src, u8 *dst);
+int u16ToU8_be(u16 src, u8 *dst);
+int u16ToU8n_be(u16 *src, int src_length, u8 *dst);
+int u8ToU32(u8 *src, u32 *dst);
+int u8ToU32_be(u8 *src, u32 *dst);
+int u32ToU8(u32 src, u8 *dst);
+int u32ToU8_be(u32 src, u8 *dst);
+int u8ToU64_be(u8 *src, u64 *dest, int size);
+int u64ToU8_be(u64 src, u8 *dest, int size);
+int attempt_function(int (*code)(void), unsigned long wait_before_retry, int
+ retry_count);
+int senseOn(void);
+int senseOff(void);
+void print_frame_short(char *label, short **matrix, int row, int column);
+short **array1dTo2d_short(short *data, int size, int columns);
+void print_frame_u16(char *label, u16 **matrix, int row, int column);
+u16 **array1dTo2d_u16(u16 *data, int size, int columns);
+u8 **array1dTo2d_u8(u8 *data, int size, int columns);
+i8 **array1dTo2d_i8(i8 *data, int size, int columns);
+void print_frame_u8(char *label, u8 **matrix, int row, int column);
+void print_frame_i8(char *label, i8 **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);
+
+/* New API */
+int fromIDtoMask(u8 id, u8 *mask, int size);
+
+#endif
diff --git a/fts_limits.h b/fts_limits.h
new file mode 100644
index 0000000..35eed02
--- /dev/null
+++ b/fts_limits.h
@@ -0,0 +1,2045 @@
+#ifndef FTS_LIMITS_H
+#define FTS_LIMITS_H
+// This is an auto generated header file
+//--->Remember to change the name of the two variables!<---
+const uint32_t myArray2_size = 24418;
+
+const uint8_t myArray2[] = {
+ 0x2A, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4C, 0x45, 0x4E, 0x47, 0x54,
+ 0x48, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x31, 0x37, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x45, 0x4E, 0x53,
+ 0x45, 0x5F, 0x4C, 0x45, 0x4E, 0x47, 0x54, 0x48, 0x2C, 0x31, 0x2C, 0x31,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x33,
+ 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x2A, 0x4B, 0x45, 0x59, 0x5F, 0x4C, 0x45, 0x4E, 0x47, 0x54,
+ 0x48, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x4B, 0x45, 0x59,
+ 0x5F, 0x4C, 0x45, 0x4E, 0x47, 0x54, 0x48, 0x2C, 0x31, 0x2C, 0x31, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x2A, 0x45, 0x4E, 0x5F, 0x4C, 0x50, 0x5F, 0x54, 0x49, 0x4D, 0x45, 0x52,
+ 0x5F, 0x43, 0x41, 0x4C, 0x49, 0x42, 0x52, 0x41, 0x54, 0x49, 0x4F, 0x4E,
+ 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x45, 0x4E, 0x5F, 0x4D, 0x55, 0x54,
+ 0x55, 0x41, 0x4C, 0x5F, 0x41, 0x55, 0x54, 0x4F, 0x54, 0x55, 0x4E, 0x45,
+ 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x45, 0x4E, 0x5F, 0x53, 0x45, 0x4C,
+ 0x46, 0x5F, 0x41, 0x55, 0x54, 0x4F, 0x54, 0x55, 0x4E, 0x45, 0x2C, 0x31,
+ 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x2A, 0x45, 0x4E, 0x5F, 0x53, 0x41, 0x56, 0x45, 0x5F,
+ 0x43, 0x58, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x43, 0x58, 0x31, 0x5F,
+ 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x43, 0x58, 0x32, 0x5F,
+ 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x49, 0x58, 0x31, 0x5F, 0x46,
+ 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A,
+ 0x49, 0x58, 0x32, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x57, 0x2C,
+ 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x49, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E,
+ 0x53, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x49, 0x58,
+ 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C,
+ 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41,
+ 0x54, 0x41, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31,
+ 0x2C, 0x32, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A,
+ 0x4D, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F,
+ 0x45, 0x41, 0x43, 0x48, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x37, 0x2C,
+ 0x33, 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x0D,
+ 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30,
+ 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30,
+ 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30,
+ 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32,
+ 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x2D,
+ 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x52, 0x41,
+ 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x45, 0x41, 0x43, 0x48, 0x5F,
+ 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x37, 0x2C, 0x33, 0x36, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D,
+ 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D, 0x0A,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C,
+ 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30,
+ 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30,
+ 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32,
+ 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x2C, 0x32, 0x30, 0x30, 0x30, 0x0D,
+ 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x49, 0x54, 0x4F,
+ 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F,
+ 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x37, 0x2C,
+ 0x33, 0x35, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x49,
+ 0x54, 0x4F, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x41, 0x44, 0x4A, 0x5F,
+ 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x31, 0x36, 0x2C,
+ 0x33, 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D, 0x0A,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30,
+ 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33,
+ 0x30, 0x30, 0x0D, 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C,
+ 0x33, 0x30, 0x30, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x53, 0x54, 0x52,
+ 0x45, 0x4E, 0x47, 0x54, 0x48, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x4D,
+ 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2D, 0x32, 0x30,
+ 0x30, 0x2C, 0x32, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55,
+ 0x43, 0x48, 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x5F,
+ 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x37, 0x2C, 0x33,
+ 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D,
+ 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D, 0x0A,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x0D,
+ 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35,
+ 0x2C, 0x35, 0x0D, 0x0A, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x35, 0x2C,
+ 0x35, 0x2C, 0x35, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55,
+ 0x43, 0x48, 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x5F,
+ 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x37, 0x2C, 0x33,
+ 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D,
+ 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D, 0x0A,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x0D,
+ 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34,
+ 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C,
+ 0x34, 0x30, 0x0D, 0x0A, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30, 0x2C, 0x34, 0x30,
+ 0x2C, 0x34, 0x30, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55,
+ 0x43, 0x48, 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x5F,
+ 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, 0x49,
+ 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x37, 0x2C, 0x33, 0x35,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D,
+ 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x0D, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F,
+ 0x55, 0x43, 0x48, 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52,
+ 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52,
+ 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x31, 0x36, 0x2C, 0x33, 0x36, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D,
+ 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31,
+ 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D, 0x0A,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C,
+ 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x0D,
+ 0x0A, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30, 0x2C, 0x31, 0x30,
+ 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41,
+ 0x54, 0x41, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E,
+ 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2D, 0x33, 0x30, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57,
+ 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x5F, 0x44, 0x41,
+ 0x54, 0x41, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E,
+ 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2D, 0x33, 0x30, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55,
+ 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F,
+ 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45,
+ 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x37, 0x2C, 0x31, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x32, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43,
+ 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54,
+ 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F,
+ 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x37, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34, 0x35, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x34,
+ 0x35, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x44, 0x45, 0x54, 0x45, 0x43,
+ 0x54, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46,
+ 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x37, 0x2C,
+ 0x31, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A,
+ 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53,
+ 0x53, 0x5F, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x5F, 0x54, 0x4F, 0x54,
+ 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F,
+ 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x37, 0x2C, 0x31, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35, 0x30, 0x30, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x35,
+ 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44,
+ 0x41, 0x54, 0x41, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49,
+ 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2D, 0x33, 0x30, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41,
+ 0x57, 0x5F, 0x4C, 0x4F, 0x57, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x5F, 0x44,
+ 0x41, 0x54, 0x41, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49,
+ 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2D, 0x33, 0x30, 0x30,
+ 0x30, 0x2C, 0x33, 0x30, 0x30, 0x30, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F,
+ 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54,
+ 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53,
+ 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x2C, 0x33, 0x36, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30,
+ 0x2C, 0x31, 0x33, 0x30, 0x2C, 0x31, 0x33, 0x30, 0x0D, 0x0A, 0x2A, 0x53,
+ 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49,
+ 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F,
+ 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C,
+ 0x33, 0x36, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D,
+ 0x0A, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30, 0x2C, 0x33, 0x30, 0x30,
+ 0x0D, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54,
+ 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x53, 0x45,
+ 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x2C, 0x33, 0x36,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30,
+ 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0D, 0x0A,
+ 0x2A, 0x53, 0x53, 0x5F, 0x44, 0x45, 0x54, 0x45, 0x43, 0x54, 0x5F, 0x54,
+ 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53,
+ 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x33, 0x36, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x0D, 0x0A, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35,
+ 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0D, 0x0A,
+};
+
+#endif
diff --git a/fts_proc.c b/fts_proc.c
new file mode 100644
index 0000000..cb030ca
--- /dev/null
+++ b/fts_proc.c
@@ -0,0 +1,3744 @@
+/*
+ *
+ **************************************************************************
+ ** STMicroelectronics **
+ **************************************************************************
+ ** marco.cali@st.com *
+ **************************************************************************
+ * *
+ * Utilities published in /proc/fts *
+ * *
+ **************************************************************************
+ **************************************************************************
+ *
+ */
+
+/*!
+ * \file fts_proc.c
+ * \brief contains the function and variables needed to publish a file node in
+ * the file system which allow to communicate with the IC from userspace
+ */
+
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include "fts.h"
+#include "fts_lib/ftsCompensation.h"
+#include "fts_lib/ftsCore.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"
+
+
+#define DRIVER_TEST_FILE_NODE "driver_test" /* /< name of file node
+ * published */
+#define CHUNK_PROC 1024 /* /< Max chunk of data printed on
+ * the sequential file in each
+ * iteration */
+#define DIAGNOSTIC_NUM_FRAME 10 /* /< number of frames reading
+ * iterations during the diagnostic
+ * test */
+
+
+
+/** @defgroup proc_file_code Proc File Node
+ * @ingroup file_nodes
+ * The /proc/fts/driver_test file node provide expose the most important API
+ * implemented into the driver to execute any possible operation into the IC \n
+ * Thanks to a series of Operation Codes, each of them, with a different set of
+ * parameter, it is possible to select a function to execute\n
+ * The result of the function is usually returned into the shell as an ASCII
+ * hex string where each byte is encoded in two chars.\n
+ * @{
+ */
+
+/* Bus operations */
+#define CMD_READ 0x00 /* /< I2C/SPI read: need
+ * to pass: byteToRead1
+ * byteToRead0
+ * (optional) dummyByte
+ * */
+#define CMD_WRITE 0x01 /* /< I2C/SPI write:
+ * need to pass: cmd[0]
+ * cmd[1] …
+ * cmd[cmdLength-1] */
+#define CMD_WRITEREAD 0x02 /* /< I2C/SPI writeRead:
+ * need to pass: cmd[0]
+ * cmd[1] …
+ * cmd[cmdLength-1]
+ * byteToRead1
+ * byteToRead0 dummyByte
+ * */
+#define CMD_WRITETHENWRITEREAD 0x03 /* /< I2C/SPI write then
+ * writeRead: need to
+ * pass: cmdSize1
+ * cmdSize2 cmd1[0]
+ * cmd1[1] …
+ * cmd1[cmdSize1-1]
+ * cmd2[0] cmd2[1] …
+ * cmd2[cmdSize2-1]
+ * byteToRead1
+ * byteToRead0 */
+#define CMD_WRITEU8UX 0x04
+ /* /< I2C/SPI
+ * writeU8UX:
+ * need to pass: cmd
+ * addrSize addr[0] …
+ * addr[addrSize-1]
+ * data[0] data[1] … */
+#define CMD_WRITEREADU8UX 0x05 /* /< I2C/SPI
+ * writeReadU8UX: need
+ * to pass: cmd addrSize
+ * addr[0] …
+ * addr[addrSize-1]
+ * byteToRead1
+ * byteToRead0
+ * hasDummyByte */
+#define CMD_WRITEU8UXTHENWRITEU8UX 0x06
+ /* /< I2C/SPI writeU8UX
+ * then writeU8UX: need
+ * to pass: cmd1
+ * addrSize1 cmd2
+ * addrSize2 addr[0] …
+ * addr[addrSize1+
+ * addrSize2-1]
+ * data[0] data[1] … */
+#define CMD_WRITEU8UXTHENWRITEREADU8UX 0x07 /* /< I2C/SPI writeU8UX
+ * then writeReadU8UX:
+ * need to pass: cmd1
+ * addrSize1 cmd2
+ * addrSize2 addr[0] …
+ * addr[addrSize1+
+ * addrSize2-1]
+ * byteToRead1
+ * byteToRead0
+ * hasDummybyte */
+#define CMD_GETLIMITSFILE 0x08 /* /< Get the Production
+ * Limits File and print
+ * its content into the
+ * shell: need to pass:
+ * path(optional)
+ * otherwise select the
+ * approach chosen at
+ * compile time */
+#define CMD_GETFWFILE 0x09 /* /< Get the FW file
+ * and print its content
+ * into the shell: need
+ * to pass: path
+ * (optional) otherwise
+ * select the approach
+ * chosen at compile
+ * time */
+#define CMD_VERSION 0x0A /* /< Get the driver
+ * version and other
+ * driver setting info
+ * */
+#define CMD_READCONFIG 0x0B /* /< Read The config
+ * memory, need to pass:
+ * addr[0] addr[1]
+ * byteToRead0
+ * byteToRead1 */
+
+
+/* GUI utils byte ver */
+#define CMD_READ_BYTE 0xF0 /* /< Byte output
+ * version of I2C/SPI
+ * read @see CMD_READ */
+#define CMD_WRITE_BYTE 0xF1 /* /< Byte output
+ * version of I2C/SPI
+ * write @see CMD_WRITE
+ * */
+#define CMD_WRITEREAD_BYTE 0xF2 /* /< Byte output
+ * version of I2C/SPI
+ * writeRead @see
+ * CMD_WRITEREAD */
+#define CMD_WRITETHENWRITEREAD_BYTE 0xF3
+ /* /< Byte output
+ * version of I2C/SPI
+ * write then writeRead
+ * @see
+ * CMD_WRITETHENWRITEREAD
+ * */
+#define CMD_WRITEU8UX_BYTE 0xF4 /* /< Byte output
+ * version of I2C/SPI
+ * writeU8UX @see
+ * CMD_WRITEU8UX */
+#define CMD_WRITEREADU8UX_BYTE 0xF5 /* /< Byte output
+ * version of I2C/SPI
+ * writeReadU8UX @see
+ * CMD_WRITEREADU8UX */
+#define CMD_WRITEU8UXTHENWRITEU8UX_BYTE 0xF6
+ /* /< Byte output
+ * version of I2C/SPI
+ * writeU8UX then
+ * writeU8UX @see
+ * CMD_WRITEU8UXTHENWRITEU8UX
+ * */
+#define CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE 0xF7
+ /* /< Byte output
+ * version of I2C/SPI
+ * writeU8UX then
+ * writeReadU8UX @see
+ * CMD_WRITEU8UXTHENWRITEREADU8UX
+ * */
+#define CMD_GETLIMITSFILE_BYTE 0xF8 /* /< Byte output
+ * version of Production
+ * Limits File @see
+ * CMD_GETLIMITSFILE */
+#define CMD_GETFWFILE_BYTE 0xF9 /* /< Byte output
+ * version of FW file
+ * need to pass: @see
+ * CMD_GETFWFILE */
+#define CMD_VERSION_BYTE 0xFA /* /< Byte output
+ * version of driver
+ * version and setting
+ * @see CMD_VERSION */
+#define CMD_CHANGE_OUTPUT_MODE 0xFF /* /< Select the output
+ * mode of the
+ * scriptless protocol,
+ * need to pass:
+ * bin_output = 1 data
+ * returned as binary,
+ * bin_output =0 data
+ * returned as hex
+ * string */
+
+/* Core/Tools */
+#define CMD_POLLFOREVENT 0x11 /* /< Poll the FIFO for
+ * an event: need to
+ * pass: eventLength
+ * event[0] event[1] …
+ * event[eventLength-1]
+ * timeToWait1
+ * timeToWait0 */
+#define CMD_SYSTEMRESET 0x12 /* /< System Reset */
+#define CMD_CLEANUP 0x13 /* /< Perform a system
+ * reset and optionally
+ * re-enable the
+ * scanning, need to
+ * pass: enableTouch */
+#define CMD_POWERCYCLE 0x14 /* /< Execute a power
+ * cycle toggling the
+ * regulators */
+#define CMD_READSYSINFO 0x15 /* /< Read the System
+ * Info information from
+ * the framebuffer, need
+ * to pass: doRequest */
+#define CMD_FWWRITE 0x16
+ /* /< Write a FW
+ * command: need to
+ * pass: cmd[0] cmd[1]
+ * … cmd[cmdLength-1] */
+#define CMD_INTERRUPT 0x17 /* /< Allow to enable or
+ * disable the
+ * interrupts, need to
+ * pass: enable (if 1
+ * will enable the
+ * interrupt) */
+#define CMD_SETSCANMODE 0x18 /* /< set Scan Mode
+ * need to pass:
+ * scanType option
+ */
+#define CMD_SAVEMPFLAG 0x19 /* /< save manually a
+ * value in the MP flag
+ * need to pass: mpflag
+ */
+
+/* Frame */
+#define CMD_GETFORCELEN 0x20 /* /< Get the number of
+ * Force channels */
+#define CMD_GETSENSELEN 0x21 /* /< Get the number of
+ * Sense channels */
+#define CMD_GETMSFRAME 0x23 /* /< Get a MS frame:
+ * need to pass:
+ * MSFrameType */
+#define CMD_GETSSFRAME 0x24 /* /< Get a SS frame:
+ * need to pass:
+ * SSFrameType */
+#define CMD_GETSYNCFRAME 0x25 /* /< Get a SS frame:
+ * need to pass:
+ * SSFrameType
+ */
+
+/* Compensation */
+#define CMD_REQCOMPDATA 0x30 /* /< Request Init data:
+ * need to pass: type */
+#define CMD_READCOMPDATAHEAD 0x31 /* /< Read Init data
+ * header: need to pass:
+ * type */
+#define CMD_READMSCOMPDATA 0x32 /* /< Read MS Init data:
+ * need to pass: type */
+#define CMD_READSSCOMPDATA 0x33 /* /< Read SS Init data:
+ * need to pass: type */
+#define CMD_READGOLDENMUTUAL 0x34 /* /< Read GoldenMutual
+ raw data */
+#define CMD_READTOTMSCOMPDATA 0x35 /* /< Read Tot MS Init
+ * data: need to pass:
+ * type */
+#define CMD_READTOTSSCOMPDATA 0x36 /* /< Read Tot SS Init
+ * data: need to pass:
+ * type */
+#define CMD_READSENSCOEFF 0x37 /* /< Read MS and SS
+ * Sensitivity
+ * Calibration
+ * Coefficients */
+
+/* FW Update */
+#define CMD_GETFWVER 0x40 /* /< Get the FW version
+ * of the IC */
+#define CMD_FLASHUNLOCK 0x42 /* /< Unlock the flash
+ * */
+#define CMD_READFWFILE 0x43 /* /< Try to read the FW
+ * file, need to pass:
+ * keep_cx */
+#define CMD_FLASHPROCEDURE 0x44 /* /< Perform a full
+ * flashing procedure:
+ * need to pass: force
+ * keep_cx */
+#define CMD_FLASHERASEUNLOCK 0x45 /* /< Unlock the erase
+ * of the flash */
+#define CMD_FLASHERASEPAGE 0x46
+ /* /< Erase page by page
+ * the flash, need to
+ * pass: keep_cx, if
+ * keep_cx>SKIP_PANEL_INIT
+ * Panel Init Page will
+ * be skipped, if
+ * >SKIP_PANEL_CX_INIT
+ * Cx and Panel Init
+ * Pages will be skipped
+ * otherwise if
+ * =ERASE_ALL all the
+ * pages will be deleted
+ * */
+
+/* MP test */
+#define CMD_ITOTEST 0x50 /* /< Perform an ITO
+ * test */
+#define CMD_INITTEST 0x51 /* /< Perform an
+ * Initialization test:
+ * need to pass: type */
+#define CMD_MSRAWTEST 0x52 /* /< Perform MS raw
+ * test: need to pass
+ * stop_on_fail */
+#define CMD_MSINITDATATEST 0x53 /* /< Perform MS Init
+ * data test: need to
+ * pass stop_on_fail */
+#define CMD_SSRAWTEST 0x54 /* /< Perform SS raw
+ * test: need to pass
+ * stop_on_fail */
+#define CMD_SSINITDATATEST 0x55 /* /< Perform SS Init
+ * data test: need to
+ * pass stop_on_fail */
+#define CMD_MAINTEST 0x56 /* /< Perform a full
+ * Mass production test:
+ * need to pass
+ * stop_on_fail saveInit
+ * */
+#define CMD_FREELIMIT 0x57 /* /< Free (if any)
+ * limit file which was
+ * loaded during any
+ * test procedure */
+
+/* Diagnostic */
+#define CMD_DIAGNOSTIC 0x60 /* /< Perform a series
+ * of commands and
+ * collect severals data
+ * to detect any
+ * malfunction */
+
+#define CMD_CHANGE_SAD 0x70 /* /< Allow to change
+ * the SAD address (for
+ * debugging) */
+#define CMD_INFOBLOCK_STATUS 0x61 /* /< Check for Info
+ * block error */
+
+/* Debug functionalities requested by Google for B1 Project */
+#define CMD_TRIGGER_FORCECAL 0x80 /* /< Trigger manually
+ * forcecal for MS and
+ * SS */
+#define CMD_BASELINE_ADAPTATION 0x81 /* /< Enable/Disable
+ * Baseline adaptation,
+ * need to pass: enable
+ * */
+#define CMD_FREQ_HOP 0x82 /* /< Enable/Disable
+ * Frequency hopping,
+ * need to pass: enable
+ * */
+#define CMD_SET_OPERATING_FREQ 0x83 /* /< Set a defined
+ * scanning frequency in
+ * Hz passed as 4 bytes
+ * in big endian, need
+ * to pass: freq3 freq2
+ * freq1 freq0 */
+#define CMD_READ_SYNC_FRAME 0x84
+ /* /< Read Sync Frame
+ * which contain MS and
+ * SS data, need to
+ * pass: frameType (this
+ * parameter can be
+ * LOAD_SYNC_FRAME_STRENGTH
+ * or LOAD_SYNC_FRAME_BASELINE)
+ * */
+
+
+#define CMD_TP_SENS_MODE 0x90 /* /< Enter/Exit in the
+ * TP Sensitivity
+ * Calibration mode,
+ * need to pass: enter
+ * (optional)saveGain */
+#define CMD_TP_SENS_SET_SCAN_MODE 0x91 /* /< Set scan mode type
+ * which should be used
+ * for the test before
+ * the stimpad is down,
+ * need to pass: type */
+#define CMD_TP_SENS_PRECAL_SS 0x92 /* /< Perform Pre
+ * Calibration for SS
+ * steps when stimpad is
+ * down */
+#define CMD_TP_SENS_PRECAL_MS 0x93 /* /< Perform Pre
+ * Calibration for MS
+ * steps when stimpad is
+ * down */
+#define CMD_TP_SENS_POSTCAL_MS 0x94 /* /< Perform Post
+ * Calibration for MS
+ * steps when stimpad is
+ * down */
+#define CMD_TP_SENS_STD 0x95 /* /< Compute the
+ * Standard deviation of
+ * a certain number of
+ * frames, need to pass:
+ * numFrames */
+
+#define CMD_FORCE_TOUCH_ACTIVE 0xA0 /* /< Prevent the driver
+ * from transitioning
+ * the ownership of the
+ * bus to SLPI
+ */
+
+static u8 bin_output; /* /< Select the output type of the scriptless
+ * protocol (binary = 1 or hex string = 0) */
+/** @}*/
+
+/** @defgroup scriptless Scriptless Protocol
+ * @ingroup proc_file_code
+ * Scriptless Protocol allows ST Software (such as FingerTip Studio etc) to
+ * communicate with the IC from an user space.
+ * This mode gives access to common bus operations (write, read etc) and
+ * support additional functionalities. \n
+ * The protocol is based on exchange of binary messages included between a
+ * start and an end byte
+ * @{
+ */
+
+#define MESSAGE_START_BYTE 0x7B /* /< start byte of each message
+ * transferred in Scriptless Mode */
+#define MESSAGE_END_BYTE 0x7D /* /< end byte of each message
+ * transferred in Scriptless Mode */
+#define MESSAGE_MIN_HEADER_SIZE 8 /* /< minimun number of bytes of the
+ * structure of a messages exchanged
+ * with host (include start/end byte,
+ * counter, actions, msg_size) */
+
+/**
+ * Possible actions that can be requested by an host
+ */
+typedef enum {
+ ACTION_WRITE = (u16) 0x0001, /* /< Bus Write
+ * */
+ ACTION_READ = (u16) 0x0002, /* /< Bus Read
+ * */
+ ACTION_WRITE_READ = (u16) 0x0003, /* /< Bus Write
+ * followed by a
+ * Read */
+ ACTION_GET_VERSION = (u16) 0x0004, /* /< Get
+ * Version of
+ * the protocol
+ * (equal to the
+ * first 2 bye
+ * of driver
+ * version) */
+ ACTION_WRITEU8UX = (u16) 0x0011, /* /< Bus Write
+ * with support
+ * to different
+ * address size
+ * */
+ ACTION_WRITEREADU8UX = (u16) 0x0012, /* /< Bus
+ * writeRead
+ * with support
+ * to different
+ * address size
+ * */
+ ACTION_WRITETHENWRITEREAD = (u16) 0x0013, /* /< Bus write
+ * followed by a
+ * writeRead */
+ ACTION_WRITEU8XTHENWRITEREADU8UX = (u16) 0x0014, /* /< Bus write
+ * followed by a
+ * writeRead
+ * with support
+ * to different
+ * address size
+ * */
+ ACTION_WRITEU8UXTHENWRITEU8UX = (u16) 0x0015, /* /< Bus write
+ * followed by a
+ * write with
+ * support to
+ * different
+ * address size
+ * */
+ ACTION_GET_FW = (u16) 0x1000, /* /< Get Fw
+ * file content
+ * used by the
+ * driver */
+ ACTION_GET_LIMIT = (u16) 0x1001 /* /< Get Limit
+ * File content
+ * used by the
+ * driver */
+} Actions;
+
+/**
+ * Struct used to contain info of the message received by the host in
+ * Scriptless mode
+ */
+typedef struct {
+ u16 msg_size; /* /< total size of the message in bytes */
+ u16 counter; /* /< counter ID to identify a message */
+ Actions action; /* /< type of operation requested by the host @see
+ * Actions */
+ u8 dummy; /* /< (optional)in case of any kind of read operations,
+ * specify if the first byte is dummy */
+} Message;
+
+/** @}*/
+
+
+
+extern TestToDo tests;
+extern SysInfo systemInfo;
+
+static int limit; /* /< store the amount of data to print into the
+ * shell */
+static int chunk; /* /< store the chuk of data that should be printed in
+ * this iteration */
+static int printed; /* /< store the amount of data already printed in the
+ * shell */
+static struct proc_dir_entry *fts_dir; /* /< reference to the directory
+ * fts under /proc */
+static u8 *driver_test_buff; /* /< pointer to an array of bytes used
+ * to store the result of the function
+ * executed */
+char buf_chunk[CHUNK_PROC] = { 0 }; /* /< buffer used to store the message
+ * info received */
+static Message mess = { 0 }; /* /< store the information of the Scriptless
+ * message received */
+
+
+/************************ SEQUENTIAL FILE UTILITIES **************************/
+/**
+ * This function is called at the beginning of the stream to a sequential file
+ * or every time into the sequential were already written PAGE_SIZE bytes and
+ * the stream need to restart
+ * @param s pointer to the sequential file on which print the data
+ * @param pos pointer to the offset where write the data
+ * @return NULL if there is no data to print or the pointer to the beginning of
+ * the data that need to be printed
+ */
+static void *fts_seq_start(struct seq_file *s, loff_t *pos)
+{
+ pr_info("%s: Entering start(), pos = %lld limit = %d printed = %d\n",
+ __func__, *pos, limit, printed);
+
+ if (driver_test_buff == NULL && *pos == 0) {
+ int size = 13 * sizeof(u8);
+
+ pr_info("%s: No data to print!\n", __func__);
+ driver_test_buff = (u8 *)kmalloc(size, GFP_KERNEL);
+ limit = scnprintf(driver_test_buff,
+ size,
+ "{ %08X }\n", ERROR_OP_NOT_ALLOW);
+ /* pr_err("%s: len = %d driver_test_buff = %s\n",
+ * __func__, limit, driver_test_buff); */
+ } else {
+ if (*pos != 0)
+ *pos += chunk - 1;
+
+ if (*pos >= limit)
+ /* pr_err("%s: Apparently, we're done.\n", __func__); */
+ return NULL;
+ }
+
+ chunk = CHUNK_PROC;
+ if (limit - *pos < CHUNK_PROC)
+ chunk = limit - *pos;
+ /* pr_err("%s: In start(),
+ * updated pos = %Ld limit = %d printed = %d chunk = %d\n",
+ * __func__, *pos, limit, printed, chunk); */
+ memset(buf_chunk, 0, CHUNK_PROC);
+ memcpy(buf_chunk, &driver_test_buff[(int)*pos], chunk);
+
+ return buf_chunk;
+}
+
+/**
+ * This function actually print a chunk amount of data in the sequential file
+ * @param s pointer to the sequential file where to print the data
+ * @param v pointer to the data to print
+ * @return 0
+ */
+static int fts_seq_show(struct seq_file *s, void *v)
+{
+ /* pr_err("%s: In show()\n", __func__); */
+ seq_write(s, (u8 *)v, chunk);
+ printed += chunk;
+ return 0;
+}
+
+/**
+ * This function update the pointer and the counters to the next data to be
+ * printed
+ * @param s pointer to the sequential file where to print the data
+ * @param v pointer to the data to print
+ * @param pos pointer to the offset where write the next data
+ * @return NULL if there is no data to print or the pointer to the beginning of
+ * the next data that need to be printed
+ */
+static void *fts_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ /* int* val_ptr; */
+ /* pr_err("%s: In next(), v = %X, pos = %Ld.\n", __func__,
+ * v, *pos); */
+ (*pos) += chunk;/* increase my position counter */
+ chunk = CHUNK_PROC;
+
+ /* pr_err("%s: In next(),
+ * updated pos = %Ld limit = %d printed = %d\n",
+ * __func__, *pos, limit,printed); */
+ if (*pos >= limit) /* are we done? */
+ return NULL;
+ else if (limit - *pos < CHUNK_PROC)
+ chunk = limit - *pos;
+
+
+ memset(buf_chunk, 0, CHUNK_PROC);
+ memcpy(buf_chunk, &driver_test_buff[(int)*pos], chunk);
+ return buf_chunk;
+}
+
+
+/**
+ * This function is called when there are no more data to print the stream
+ *need to be terminated or when PAGE_SIZE data were already written into the
+ *sequential file
+ * @param s pointer to the sequential file where to print the data
+ * @param v pointer returned by fts_seq_next
+ */
+static void fts_seq_stop(struct seq_file *s, void *v)
+{
+ /* pr_err("%s: Entering stop().\n", __func__); */
+
+ if (v) {
+ /* pr_err("%s: v is %X.\n", __func__, v); */
+ } else {
+ /* pr_err("%s: v is null.\n", __func__); */
+ limit = 0;
+ chunk = 0;
+ printed = 0;
+ if (driver_test_buff != NULL) {
+ /* pr_err("%s: Freeing and clearing driver_test_buff.\n",
+ * __func__); */
+ kfree(driver_test_buff);
+ driver_test_buff = NULL;
+ } else {
+ /* pr_err("%s: driver_test_buff is already null.\n",
+ * __func__); */
+ }
+ }
+}
+
+/**
+ * Struct where define and specify the functions which implements the flow for
+ * writing on a sequential file
+ */
+static const struct seq_operations fts_seq_ops = {
+ .start = fts_seq_start,
+ .next = fts_seq_next,
+ .stop = fts_seq_stop,
+ .show = fts_seq_show
+};
+
+/**
+ * This function open a sequential file
+ * @param inode Inode in the file system that was called and triggered this
+ * function
+ * @param file file associated to the file node
+ * @return error code, 0 if success
+ */
+static int fts_driver_test_open(struct inode *inode, struct file *file)
+{
+ struct fts_ts_info *info = dev_get_drvdata(getDev());
+ int retval;
+
+ if (!info) {
+ pr_err("%s: Unable to access driver data\n", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (!mutex_trylock(&info->diag_cmd_lock)) {
+ pr_err("%s: Blocking concurrent access\n", __func__);
+ retval = -EBUSY;
+ goto exit;
+ }
+
+ /* Allowing only a single process to open diag procfs node */
+ if (info->diag_node_open == true) {
+ pr_err("%s: Blocking multiple open\n", __func__);
+ retval = -EBUSY;
+ goto unlock;
+ }
+
+ retval = seq_open(file, &fts_seq_ops);
+ if(!retval) {
+ info->diag_node_open = true;
+ }
+
+unlock:
+ mutex_unlock(&info->diag_cmd_lock);
+exit:
+ return retval;
+};
+
+/**
+ * This function closes a sequential file
+ * @param inode Inode in the file system that was called and triggered this
+ * function
+ * @param file file associated to the file node
+ * @return error code, 0 if success
+ */
+static int fts_driver_test_release(struct inode *inode, struct file *file)
+{
+ struct fts_ts_info *info = dev_get_drvdata(getDev());
+ int retval;
+
+ if (info)
+ mutex_lock(&info->diag_cmd_lock);
+ else
+ pr_err("%s: Unable to access driver data\n", __func__);
+
+ retval = seq_release(inode, file);
+
+ if (info) {
+ info->diag_node_open = false;
+ mutex_unlock(&info->diag_cmd_lock);
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+
+/**************************** DRIVER TEST ************************************/
+
+/** @addtogroup proc_file_code
+ * @{
+ */
+
+/**
+ * Receive the OP code and the inputs from shell when the file node is called,
+ * parse it and then execute the corresponding function
+ * echo cmd+parameters > /proc/fts/driver_test to execute the select command
+ * cat /proc/fts/driver_test to obtain the result into the
+ * shell \n
+ * the string returned in the shell is made up as follow: \n
+ * { = start byte \n
+ * the answer content and format strictly depend on the cmd executed. In
+ * general can be: an HEX string or a byte array (e.g in case of 0xF- commands)
+ * \n
+ * } = end byte \n
+ */
+static ssize_t fts_driver_test_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ int numberParam = 0;
+ struct fts_ts_info *info = dev_get_drvdata(getDev());
+ char *p = NULL;
+ char *pbuf = NULL;
+ char path[100] = { 0 };
+ int res = -1, j, index = 0;
+ u8 report = 0;
+ int size = 6;
+ int temp, byte_call = 0;
+ u16 byteToRead = 0;
+ u32 fileSize = 0;
+ u8 *readData = NULL;
+ u8 *cmd = NULL; /* worst case needs count bytes */
+ u32 maxNum_cmd = 0;
+ u32 *funcToTest = NULL;
+ u32 maxNum_funcToTest = 0;
+ u64 addr = 0;
+ MutualSenseFrame frameMS;
+ MutualSenseFrame deltas;
+ SelfSenseFrame frameSS;
+ u8 error_to_search[2] = { EVT_TYPE_ERROR_OSC_TRIM,
+ EVT_TYPE_ERROR_AOFFSET_TRIM };
+
+ DataHeader dataHead;
+ MutualSenseData compData;
+ SelfSenseData comData;
+ TotMutualSenseData totCompData;
+ TotSelfSenseData totComData;
+ MutualSenseCoeff msCoeff;
+ SelfSenseCoeff ssCoeff;
+ GoldenMutualRawData gmRawData;
+ int meanNorm = 0, meanEdge = 0;
+
+ u64 address;
+
+ Firmware fw;
+ LimitFile lim;
+ const char *limits_file = info->board->limits_name;
+
+ if (!info) {
+ pr_err("%s: Unable to access driver data\n", __func__);
+ count = -ENODEV;
+ goto exit;
+ }
+
+ mess.dummy = 0;
+ mess.action = 0;
+ mess.msg_size = 0;
+
+ if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
+ res = ERROR_BUS_WR;
+ pr_err("%s: bus is not accessible.\n", __func__);
+ if (driver_test_buff)
+ limit = scnprintf(driver_test_buff, size, "{ %08X }\n",
+ res);
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+ goto ERROR;
+ }
+
+ if (count < 2) {
+ res = ERROR_OP_NOT_ALLOW;
+ goto ERROR;
+ }
+
+ /* alloc one more space to store '\0' at the end
+ * for "echo -n CMDs" case
+ */
+ pbuf = kmalloc((count + 1) * sizeof(*pbuf), GFP_KERNEL);
+ if (!pbuf) {
+ res = ERROR_ALLOC;
+ goto ERROR;
+ }
+ pbuf[count] = '\0';
+
+
+ maxNum_funcToTest = (count + 1) / 3;
+ funcToTest = kmalloc_array(maxNum_funcToTest, sizeof(*funcToTest),
+ GFP_KERNEL);
+ if (!funcToTest) {
+ res = ERROR_ALLOC;
+ goto ERROR;
+ }
+
+ /*for(temp = 0; temp<count; temp++){
+ * pr_err("p[%d] = %02X\n", temp, p[temp]);
+ * }*/
+ if (access_ok(VERIFY_READ, buf, count) < OK ||
+ copy_from_user(pbuf, buf, count) != 0) {
+ res = ERROR_ALLOC;
+ goto END;
+ }
+
+ maxNum_cmd = count;
+ cmd = (u8 *)kmalloc_array(maxNum_cmd, sizeof(u8), GFP_KERNEL);
+ if (cmd == NULL) {
+ res = ERROR_ALLOC;
+ pr_err("%s: Impossible allocate memory... ERROR %08X!\n",
+ __func__, res);
+ goto ERROR;
+ }
+
+ p = pbuf;
+ if (count > MESSAGE_MIN_HEADER_SIZE - 1 && p[0] == MESSAGE_START_BYTE) {
+ pr_info("Enter in Byte Mode!\n");
+ byte_call = 1;
+ mess.msg_size = (p[1] << 8) | p[2];
+ mess.counter = (p[3] << 8) | p[4];
+ mess.action = (p[5] << 8) | p[6];
+ pr_info("Message received: size = %d, counter_id = %d, action = %04X\n",
+ mess.msg_size, mess.counter, mess.action);
+ size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 error code */
+ if (count < mess.msg_size || p[count - 2] != MESSAGE_END_BYTE) {
+ pr_err("number of byte received or end byte wrong! msg_size = %d != %zu, last_byte = %02X != %02X ... ERROR %08X\n",
+ mess.msg_size, count, p[count - 1],
+ MESSAGE_END_BYTE, ERROR_OP_NOT_ALLOW);
+ res = ERROR_OP_NOT_ALLOW;
+ goto END;
+ } else {
+ numberParam = mess.msg_size - MESSAGE_MIN_HEADER_SIZE +
+ 1; /* +1 because put the internal
+ * op code */
+ size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 send also
+ * the first 2
+ * lsb of the
+ * error code */
+ switch (mess.action) {
+ case ACTION_READ:
+ /* numberParam =
+ * mess.msg_size-MESSAGE_MIN_HEADER_SIZE+1; */
+ cmd[0] = funcToTest[0] = CMD_READ_BYTE;
+ break;
+
+ case ACTION_WRITE:
+ cmd[0] = funcToTest[0] = CMD_WRITE_BYTE;
+ break;
+
+ case ACTION_WRITE_READ:
+ cmd[0] = funcToTest[0] = CMD_WRITEREAD_BYTE;
+ break;
+
+ case ACTION_GET_VERSION:
+ cmd[0] = funcToTest[0] = CMD_VERSION_BYTE;
+ break;
+
+ case ACTION_WRITETHENWRITEREAD:
+ cmd[0] = funcToTest[0] =
+ CMD_WRITETHENWRITEREAD_BYTE;
+ break;
+
+ case ACTION_WRITEU8UX:
+ cmd[0] = funcToTest[0] = CMD_WRITEU8UX_BYTE;
+ break;
+
+ case ACTION_WRITEREADU8UX:
+ cmd[0] = funcToTest[0] = CMD_WRITEREADU8UX_BYTE;
+ break;
+
+ case ACTION_WRITEU8UXTHENWRITEU8UX:
+ cmd[0] = funcToTest[0] =
+ CMD_WRITEU8UXTHENWRITEU8UX_BYTE;
+ break;
+
+ case ACTION_WRITEU8XTHENWRITEREADU8UX:
+ cmd[0] = funcToTest[0] =
+ CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE;
+ break;
+
+ case ACTION_GET_FW:
+ cmd[0] = funcToTest[0] = CMD_GETFWFILE_BYTE;
+ break;
+
+ case ACTION_GET_LIMIT:
+ cmd[0] = funcToTest[0] = CMD_GETLIMITSFILE_BYTE;
+ break;
+
+ default:
+ pr_err("Invalid Action = %d ... ERROR %08X\n",
+ mess.action, ERROR_OP_NOT_ALLOW);
+ res = ERROR_OP_NOT_ALLOW;
+ goto END;
+ }
+
+ if (numberParam - 1 != 0)
+ memcpy(&cmd[1], &p[7], numberParam - 1);
+ /* -1 because i need to exclude the cmd[0] */
+ }
+ } else {
+ u8 result;
+ char *token;
+ char *path_token = NULL;
+ size_t token_len;
+
+ /* newline case at last char */
+ if (p[count - 1] == '\n')
+ p[count - 1] = '\0';
+
+ /* Parse the input string to retrieve 2 hex-digit width
+ * cmds/args separated by one or more spaces, except for
+ * the fw/limits file name.
+ */
+ while (p &&
+ numberParam < min(maxNum_cmd, maxNum_funcToTest)) {
+
+ while (isspace(*p))
+ p++;
+
+ token = strsep(&p, " ");
+
+ if (!token || *token == '\0')
+ break;
+
+ token_len = strlen(token);
+
+ /* break the loop to handle FW/LIMITS path */
+ if (numberParam == 1 &&
+ (funcToTest[0] == CMD_GETFWFILE ||
+ funcToTest[0] == CMD_GETLIMITSFILE)) {
+ path_token = token;
+ break;
+ }
+
+ if (token_len != 2) {
+ pr_err("bad len. len=%zu\n", token_len);
+ res = ERROR_OP_NOT_ALLOW;
+ goto ERROR;
+ }
+
+ if (kstrtou8(token, 16, &result)) {
+ /* Conversion failed due to bad input.
+ * Discard the entire buffer.
+ */
+ pr_err("bad input\n");
+ res = ERROR_OP_NOT_ALLOW;
+ goto ERROR;
+ }
+
+ /* found a valid cmd/args */
+ cmd[numberParam] = funcToTest[numberParam] = result;
+ pr_info("functionToTest[%d] = %02X cmd[%d] = %02X\n",
+ numberParam, funcToTest[numberParam],
+ numberParam, cmd[numberParam]);
+ numberParam++;
+ }
+
+ /* FW/LIMITS path */
+ if (path_token && strlen(path_token)) {
+ strlcpy(path, path_token, sizeof(path));
+ numberParam++;
+ }
+
+ if (numberParam == 0) {
+ pr_err("Found invalid cmd/arg\n");
+ res = ERROR_OP_NOT_ALLOW;
+ goto END;
+ }
+ }
+
+ fw.data = NULL;
+ lim.data = NULL;
+
+ pr_info("Number of Parameters = %d\n", numberParam);
+
+ /* elaborate input */
+ if (numberParam >= 1) {
+ switch (funcToTest[0]) {
+ case CMD_VERSION_BYTE:
+ pr_info("%s: Get Version Byte\n", __func__);
+ byteToRead = 2;
+ mess.dummy = 0;
+ readData = (u8 *)kmalloc(byteToRead * sizeof(u8),
+ GFP_KERNEL);
+ size += byteToRead;
+ if (readData != NULL) {
+ readData[0] = (u8)(FTS_TS_DRV_VER >> 24);
+ readData[1] = (u8)(FTS_TS_DRV_VER >> 16);
+ pr_info("%s: Version = %02X%02X\n",
+ __func__, readData[0], readData[1]);
+ res = OK;
+ } else {
+ res = ERROR_ALLOC;
+ pr_err("%s: Impossible allocate memory... ERROR %08X\n",
+ __func__, res);
+ }
+ break;
+
+
+ case CMD_VERSION:
+ byteToRead = 2 * sizeof(u32);
+ mess.dummy = 0;
+ readData = (u8 *)kmalloc(byteToRead * sizeof(u8),
+ GFP_KERNEL);
+ u32ToU8_be(FTS_TS_DRV_VER, readData);
+ fileSize = 0;
+ /* first two bytes bitmask of features enabled in the
+ * IC, second two bytes bitmask of features enabled in
+ * the driver */
+
+#ifdef FW_H_FILE
+ fileSize |= 0x00010000;
+#endif
+
+#ifdef LIMITS_H_FILE
+ fileSize |= 0x00020000;
+#endif
+
+#ifdef USE_ONE_FILE_NODE
+ fileSize |= 0x00040000;
+#endif
+
+#ifdef FW_UPDATE_ON_PROBE
+ fileSize |= 0x00080000;
+#endif
+
+#ifdef PRE_SAVED_METHOD
+ fileSize |= 0x00100000;
+#endif
+
+#ifdef COMPUTE_INIT_METHOD
+ fileSize |= 0x00200000;
+#endif
+
+#ifdef USE_GESTURE_MASK
+ fileSize |= 0x00100000;
+#endif
+
+#ifdef I2C_INTERFACE
+ fileSize |= 0x00200000;
+#else
+ if (getClient() && (getClient()->mode & SPI_3WIRE) == 0)
+ fileSize |= 0x00400000;
+#endif
+
+#ifdef PHONE_KEY /* it is a feature enabled in the config of the chip */
+ fileSize |= 0x00000100;
+#endif
+
+#ifdef GESTURE_MODE
+ fromIDtoMask(FEAT_SEL_GESTURE, (u8 *)&fileSize, 4);
+#endif
+
+
+#ifdef GRIP_MODE
+ fromIDtoMask(FEAT_SEL_GRIP, (u8 *)&fileSize, 4);
+#endif
+
+#ifdef CHARGER_MODE
+ fromIDtoMask(FEAT_SEL_CHARGER, (u8 *)&fileSize, 4);
+#endif
+
+#ifdef GLOVE_MODE
+ fromIDtoMask(FEAT_SEL_GLOVE, (u8 *)&fileSize, 4);
+#endif
+
+
+#ifdef COVER_MODE
+ fromIDtoMask(FEAT_SEL_COVER, (u8 *)&fileSize, 4);
+#endif
+
+#ifdef STYLUS_MODE
+ fromIDtoMask(FEAT_SEL_STYLUS, (u8 *)&fileSize, 4);
+#endif
+
+ u32ToU8_be(fileSize, &readData[4]);
+ res = OK;
+ size += (byteToRead * sizeof(u8));
+ break;
+
+ case CMD_WRITEREAD:
+ case CMD_WRITEREAD_BYTE:
+ if (numberParam >= 5) { /* need to pass: cmd[0] cmd[1]
+ * … cmd[cmdLength-1]
+ * byteToRead1 byteToRead0
+ * dummyByte */
+ temp = numberParam - 4;
+ if (cmd[numberParam - 1] == 0)
+ mess.dummy = 0;
+ else
+ mess.dummy = 1;
+
+ u8ToU16_be(&cmd[numberParam - 3], &byteToRead);
+ pr_info("bytesToRead = %d\n",
+ byteToRead + mess.dummy);
+
+ readData = (u8 *)kmalloc((byteToRead +
+ mess.dummy) *
+ sizeof(u8),
+ GFP_KERNEL);
+ res = fts_writeRead_heap(&cmd[1], temp,
+ readData, byteToRead + mess.dummy);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_WRITE:
+ case CMD_WRITE_BYTE:
+ if (numberParam >= 2) { /* need to pass: cmd[0] cmd[1]
+ * … cmd[cmdLength-1] */
+ temp = numberParam - 1;
+
+ res = fts_write_heap(&cmd[1], temp);
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READ:
+ case CMD_READ_BYTE:
+ if (numberParam >= 3) { /* need to pass: byteToRead1
+ * byteToRead0 (optional)
+ * dummyByte */
+ if (numberParam == 3 ||
+ (numberParam == 4 &&
+ cmd[numberParam - 1] == 0))
+ mess.dummy = 0;
+ else
+ mess.dummy = 1;
+ u8ToU16_be(&cmd[1], &byteToRead);
+ readData = (u8 *)kmalloc((byteToRead +
+ mess.dummy) *
+ sizeof(u8),
+ GFP_KERNEL);
+ res = fts_read_heap(readData, byteToRead +
+ mess.dummy);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_WRITETHENWRITEREAD:
+ case CMD_WRITETHENWRITEREAD_BYTE:
+ /* need to pass: cmdSize1 cmdSize2 cmd1[0] cmd1[1] …
+ * cmd1[cmdSize1-1] cmd2[0] cmd2[1] … cmd2[cmdSize2-1]
+ * byteToRead1 byteToRead0 */
+ if (numberParam >= 6) {
+ u8ToU16_be(&cmd[numberParam - 2], &byteToRead);
+ readData = (u8 *)kmalloc(byteToRead *
+ sizeof(u8),
+ GFP_KERNEL);
+ res = fts_writeThenWriteRead_heap(
+ &cmd[3], cmd[1],
+ &cmd[3 + (int)cmd[1]], cmd[2],
+ readData, byteToRead);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_WRITEU8UX:
+ case CMD_WRITEU8UX_BYTE:
+ /* need to pass:
+ * cmd addrSize addr[0] … addr[addrSize-1]
+ * data[0] data[1] … */
+ if (numberParam >= 4) {
+ if (cmd[2] <= sizeof(u64)) {
+ u8ToU64_be(&cmd[3], &addr, cmd[2]);
+ pr_info("addr = %llx\n", addr);
+ res = fts_writeU8UX(cmd[1], cmd[2],
+ addr,
+ &cmd[3 + cmd[2]],
+ (numberParam - cmd[2] - 3));
+ } else {
+ pr_err("Wrong address size!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ case CMD_WRITEREADU8UX:
+ case CMD_WRITEREADU8UX_BYTE:
+ /* need to pass:
+ * cmd addrSize addr[0] … addr[addrSize-1]
+ * byteToRead1 byteToRead0 hasDummyByte */
+ if (numberParam >= 6) {
+ if (cmd[2] <= sizeof(u64)) {
+ u8ToU64_be(&cmd[3], &addr, cmd[2]);
+ u8ToU16_be(&cmd[numberParam - 3],
+ &byteToRead);
+ readData = (u8 *)kmalloc(byteToRead *
+ sizeof(u8),
+ GFP_KERNEL);
+ pr_info("addr = %llx byteToRead = %d\n",
+ addr, byteToRead);
+ res = fts_writeReadU8UX(cmd[1], cmd[2],
+ addr, readData,
+ byteToRead,
+ cmd[numberParam - 1]);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong address size!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_WRITEU8UXTHENWRITEU8UX:
+ case CMD_WRITEU8UXTHENWRITEU8UX_BYTE:
+ /* need to pass:
+ * cmd1 addrSize1 cmd2 addrSize2 addr[0] …
+ * addr[addrSize1+addrSize2-1] data[0] data[1] … */
+ if (numberParam >= 6) {
+ if ((cmd[2] + cmd[4]) <= sizeof(u64)) {
+ u8ToU64_be(&cmd[5], &addr, cmd[2] +
+ cmd[4]);
+
+ pr_info("addr = %llx\n", addr);
+ res = fts_writeU8UXthenWriteU8UX(cmd[1],
+ cmd[2], cmd[3],
+ cmd[4], addr,
+ &cmd[5 + cmd[2] + cmd[4]],
+ (numberParam - cmd[2]
+ - cmd[4] - 5));
+ } else {
+ pr_err("Wrong address size!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_WRITEU8UXTHENWRITEREADU8UX:
+ case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE:
+ /* need to pass:
+ * cmd1 addrSize1 cmd2 addrSize2 addr[0] …
+ * addr[addrSize1+addrSize2-1] byteToRead1 byteToRead0
+ * hasDummybyte */
+ if (numberParam >= 8) {
+ if ((cmd[2] + cmd[4]) <= sizeof(u64)) {
+ u8ToU64_be(&cmd[5], &addr, cmd[2] +
+ cmd[4]);
+ pr_info("%s: cmd[5] = %02X, addr = %llx\n",
+ __func__, cmd[5], addr);
+ u8ToU16_be(&cmd[numberParam - 3],
+ &byteToRead);
+ readData = (u8 *)kmalloc(byteToRead *
+ sizeof(u8),
+ GFP_KERNEL);
+ res = fts_writeU8UXthenWriteReadU8UX(
+ cmd[1], cmd[2], cmd[3], cmd[4],
+ addr,
+ readData, byteToRead,
+ cmd[numberParam - 1]);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong total address size!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ break;
+
+ case CMD_CHANGE_OUTPUT_MODE:
+ /* need to pass: bin_output */
+ if (numberParam >= 2) {
+ bin_output = cmd[1];
+ pr_info("Setting Scriptless output mode: %d\n",
+ bin_output);
+ res = OK;
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FWWRITE:
+ if (numberParam >= 3) { /* need to pass: cmd[0] cmd[1]
+ * … cmd[cmdLength-1] */
+ if (numberParam >= 2) {
+ temp = numberParam - 1;
+ res = fts_writeFwCmd_heap(&cmd[1],
+ temp);
+ } else {
+ pr_err("Wrong parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_INTERRUPT:
+ /* need to pass: enable */
+ if (numberParam >= 2) {
+ if (cmd[1] == 1)
+ res = fts_enableInterrupt(true);
+ else
+ res = fts_enableInterrupt(false);
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SETSCANMODE:
+ /* need to pass: scanMode option */
+ if (numberParam >= 3)
+ res = setScanMode(cmd[1], cmd[2]);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SAVEMPFLAG:
+ /* need to pass: mpflag */
+ if (numberParam == 2)
+ res = saveMpFlag(cmd[1]);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READCONFIG:
+ if (numberParam == 5) { /* need to pass: addr[0]
+ * addr[1] byteToRead1
+ * byteToRead0 */
+ byteToRead = ((funcToTest[3] << 8) |
+ funcToTest[4]);
+ readData = (u8 *)kmalloc(byteToRead *
+ sizeof(u8),
+ GFP_KERNEL);
+ res = readConfig((u16)((((u8)funcToTest[1] &
+ 0x00FF) << 8) +
+ ((u8)funcToTest[2] &
+ 0x00FF)),
+ readData, byteToRead);
+ size += (byteToRead * sizeof(u8));
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_POLLFOREVENT:
+ if (numberParam >= 5) { /* need to pass: eventLength
+ * event[0] event[1] …
+ * event[eventLength-1]
+ * timeTowait1 timeTowait0 */
+ temp = (int)funcToTest[1];
+ if (numberParam == 5 + (temp - 1) &&
+ temp != 0) {
+ readData = (u8 *)kmalloc(
+ FIFO_EVENT_SIZE * sizeof(u8),
+ GFP_KERNEL);
+ res = pollForEvent(
+ (int *)&funcToTest[2], temp,
+ readData,
+ ((funcToTest[temp + 2] &
+ 0x00FF) << 8) +
+ (funcToTest[temp + 3] &
+ 0x00FF));
+ if (res >= OK)
+ res = OK; /* pollForEvent
+ * return the
+ * number of
+ * error found
+ * */
+ size += (FIFO_EVENT_SIZE * sizeof(u8));
+ byteToRead = FIFO_EVENT_SIZE;
+ } else {
+ pr_err("Wrong parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SYSTEMRESET:
+ res = fts_system_reset();
+
+ break;
+
+ case CMD_READSYSINFO:
+ if (numberParam == 2) /* need to pass: doRequest */
+ res = readSysInfo(funcToTest[1]);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ break;
+
+ case CMD_CLEANUP:/* TOUCH ENABLE/DISABLE */
+ if (numberParam == 2) /* need to pass: enableTouch */
+ res = cleanUp(funcToTest[1]);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ break;
+
+ case CMD_GETFORCELEN: /* read number Tx channels */
+ temp = getForceLen();
+ if (temp < OK)
+ res = temp;
+ else {
+ size += (1 * sizeof(u8));
+ res = OK;
+ }
+ break;
+
+ case CMD_GETSENSELEN: /* read number Rx channels */
+ temp = getSenseLen();
+ if (temp < OK)
+ res = temp;
+ else {
+ size += (1 * sizeof(u8));
+ res = OK;
+ }
+ break;
+
+
+ case CMD_GETMSFRAME:
+ if (numberParam == 2) {
+ pr_info("Get 1 MS Frame\n");
+ /* setScanMode(SCAN_MODE_ACTIVE, 0xFF);
+ * mdelay(WAIT_FOR_FRESH_FRAMES);
+ * setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ * mdelay(WAIT_AFTER_SENSEOFF);
+ */
+ /* flushFIFO(); //delete the events related to
+ * some touch (allow to call this function while
+ * touching the screen without having a flooding
+ * of the FIFO) */
+ res = getMSFrame3((MSFrameType)cmd[1],
+ &frameMS);
+ if (res < 0)
+ pr_err("Error while taking the MS frame... ERROR %08X\n",
+ res);
+
+ else {
+ pr_info("The frame size is %d words\n",
+ res);
+ size += (res * sizeof(short) + 2);
+ /* +2 to add force and sense channels
+ * 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);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ /*read self raw*/
+ case CMD_GETSSFRAME:
+ if (numberParam == 2) {
+ /* pr_info("Get 1 SS Frame\n"); */
+ /* flushFIFO(); //delete the events related to
+ * some touch (allow to call this function while
+ * touching the screen without having a flooding
+ * of the FIFO) */
+ /* setScanMode(SCAN_MODE_ACTIVE, 0xFF);
+ * mdelay(WAIT_FOR_FRESH_FRAMES);
+ * setScanMode(SCAN_MODE_ACTIVE, 0x00);
+ * mdelay(WAIT_AFTER_SENSEOFF);
+ */
+ res = getSSFrame3((SSFrameType)cmd[1],
+ &frameSS);
+
+ if (res < OK)
+ pr_err("Error while taking the SS frame... ERROR %08X\n",
+ res);
+
+ else {
+ pr_info("The frame size is %d words\n",
+ res);
+ size += (res * sizeof(short) + 2);
+ /* +2 to add force and sense channels
+ * 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);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_GETSYNCFRAME:
+ /* need to pass: frameType (this parameter can be
+ * one of LOAD_SYNC_FRAME_X)
+ */
+ if (numberParam == 2) {
+ pr_info("Reading Sync Frame...\n");
+ res = getSyncFrame(cmd[1], &frameMS, &frameSS);
+ if (res < OK)
+ pr_err("Error while taking the Sync Frame frame... ERROR %08X\n",
+ res);
+
+ else {
+ pr_info("The total frames size is %d words\n",
+ res);
+ size += (res * sizeof(short) + 4);
+ /* +4 to add force and sense channels
+ * for MS and SS.
+ * Set res to OK because if getSyncFrame
+ * 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);
+ 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);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_REQCOMPDATA: /* request comp data */
+ if (numberParam == 2) {
+ pr_info("Requesting Compensation Data\n");
+ res = requestHDMDownload(cmd[1]);
+
+ if (res < OK)
+ pr_err("Error requesting compensation data ERROR %08X\n",
+ res);
+ else
+ pr_info("Requesting Compensation Data Finished!\n");
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READCOMPDATAHEAD: /* read comp data header */
+ if (numberParam == 2) {
+ pr_info("Requesting Compensation Data\n");
+ res = requestHDMDownload(cmd[1]);
+ if (res < OK)
+ pr_err("Error requesting compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("Requesting Compensation Data Finished!\n");
+ res = readHDMHeader((u8)funcToTest[1],
+ &dataHead, &address);
+ if (res < OK)
+ pr_err("Read Compensation Data Header ERROR %08X\n",
+ res);
+ else {
+ pr_info("Read Compensation Data Header OK!\n");
+ size += (1 * sizeof(u8));
+ }
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ case CMD_READMSCOMPDATA:/* read mutual comp data */
+ if (numberParam == 2) {
+ pr_info("Get MS Compensation Data\n");
+ res = readMutualSenseCompensationData(cmd[1],
+ &compData);
+
+ if (res < OK)
+ pr_err("Error reading MS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("MS Compensation Data Reading Finished!\n");
+ size = ((compData.node_data_size + 10) *
+ sizeof(i8));
+ print_frame_i8("MS Data (Cx2) =",
+ array1dTo2d_i8(
+ compData.node_data,
+ compData.node_data_size,
+ compData.header.sense_node),
+ compData.header.force_node,
+ compData.header.sense_node);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READSSCOMPDATA:
+ if (numberParam == 2) { /* read self comp data */
+ pr_info("Get SS Compensation Data...\n");
+ res = readSelfSenseCompensationData(cmd[1],
+ &comData);
+ if (res < OK)
+ pr_err("Error reading SS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("SS Compensation Data Reading Finished!\n");
+ size = ((comData.header.force_node +
+ comData.header.sense_node) *
+ 2 + 15) *
+ sizeof(i8);
+ print_frame_i8("SS Data Ix2_fm = ",
+ array1dTo2d_i8(
+ comData.ix2_fm,
+ comData.header.force_node,
+ comData.header.force_node),
+ 1,
+ comData.header.force_node);
+ print_frame_i8("SS Data Cx2_fm = ",
+ array1dTo2d_i8(
+ comData.cx2_fm,
+ comData.header.force_node,
+ comData.header.force_node),
+ 1,
+ comData.header.force_node);
+ print_frame_i8("SS Data Ix2_sn = ",
+ array1dTo2d_i8(
+ comData.ix2_sn,
+ comData.header.sense_node,
+ comData.header.sense_node),
+ 1,
+ comData.header.sense_node);
+ print_frame_i8("SS Data Cx2_sn = ",
+ array1dTo2d_i8(
+ comData.cx2_sn,
+ comData.header.sense_node,
+ comData.header.sense_node),
+ 1,
+ comData.header.sense_node);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READGOLDENMUTUAL:
+ if (numberParam != 1) {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ break;
+ }
+
+ pr_info("Get Golden Mutual Raw data\n");
+
+ res = readGoldenMutualRawData(&gmRawData);
+ if (res < OK) {
+ pr_err("Err reading GM data %08X\n", res);
+ break;
+ }
+
+ pr_info("GM data reading Finished!\n");
+
+ size = (6 + gmRawData.data_size) * sizeof(u16);
+
+ print_frame_short("Golden Mutual Data =",
+ array1dTo2d_short(
+ gmRawData.data,
+ gmRawData.data_size,
+ gmRawData.hdr.ms_s_len),
+ gmRawData.hdr.ms_f_len,
+ gmRawData.hdr.ms_s_len);
+ break;
+
+ case CMD_READTOTMSCOMPDATA: /* read mutual comp data */
+ if (numberParam == 2) {
+ pr_info("Get TOT MS Compensation Data\n");
+ res = readTotMutualSenseCompensationData(cmd[1],
+ &totCompData);
+
+ if (res < OK)
+ pr_err("Error reading TOT MS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("TOT MS Compensation Data Reading Finished!\n");
+ size = (totCompData.node_data_size *
+ sizeof(short) + 9);
+ print_frame_short("MS Data (TOT Cx) =",
+ array1dTo2d_short(
+ totCompData.node_data,
+ totCompData.node_data_size,
+ totCompData.header.sense_node),
+ totCompData.header.force_node,
+ totCompData.header.sense_node);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READTOTSSCOMPDATA:
+ if (numberParam == 2) { /* read self comp data */
+ pr_info("Get TOT SS Compensation Data...\n");
+ res = readTotSelfSenseCompensationData(cmd[1],
+ &totComData);
+ if (res < OK)
+ pr_err("Error reading TOT SS compensation data ERROR %08X\n",
+ res);
+ else {
+ pr_info("TOT SS Compensation Data Reading Finished!\n");
+ size = ((totComData.header.force_node +
+ totComData.header.sense_node) *
+ 2 *
+ sizeof(short) + 9);
+ print_frame_u16("SS Data TOT Ix_fm = ",
+ array1dTo2d_u16(
+ totComData.ix_fm,
+ totComData.header.force_node,
+ totComData.header.force_node),
+ 1,
+ totComData.header.force_node);
+ print_frame_short(
+ "SS Data TOT Cx_fm = ",
+ array1dTo2d_short(
+ totComData.cx_fm,
+ totComData.header.force_node,
+ totComData.header.force_node),
+ 1,
+ totComData.header.force_node);
+ print_frame_u16("SS Data TOT Ix_sn = ",
+ array1dTo2d_u16(
+ totComData.ix_sn,
+ totComData.header.sense_node,
+ totComData.header.sense_node),
+ 1,
+ totComData.header.sense_node);
+ print_frame_short(
+ "SS Data TOT Cx_sn = ",
+ array1dTo2d_short(
+ totComData.cx_sn,
+ totComData.header.sense_node,
+ totComData.header.sense_node),
+ 1,
+ totComData.header.sense_node);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READSENSCOEFF:
+ /* read MS and SS Sensitivity Coefficients */
+ pr_info("Get Sensitivity Calibration Coefficients...\n");
+ res = readSensitivityCoefficientsData(&msCoeff,
+ &ssCoeff);
+ if (res < OK)
+ pr_err("Error reading Sensitivity Calibration Coefficients ERROR %08X\n",
+ res);
+ else {
+ pr_info("Sensitivity Calibration Coefficients Reading Finished!\n");
+ size += (((msCoeff.node_data_size) +
+ ssCoeff.header.force_node +
+ ssCoeff.header.sense_node) *
+ sizeof(u8) + 4);
+ print_frame_u8("MS Sensitivity Coeff = ",
+ array1dTo2d_u8(msCoeff.ms_coeff,
+ msCoeff.
+ node_data_size,
+ msCoeff.header.
+ sense_node),
+ msCoeff.header.force_node,
+ msCoeff.header.sense_node);
+ print_frame_u8("SS Sensitivity Coeff force = ",
+ array1dTo2d_u8(
+ ssCoeff.ss_force_coeff,
+ ssCoeff.header.
+ force_node, 1),
+ ssCoeff.header.force_node, 1);
+ print_frame_u8("SS Sensitivity Coeff sense = ",
+ array1dTo2d_u8(
+ ssCoeff.ss_sense_coeff,
+ ssCoeff.header.
+ sense_node,
+ ssCoeff.header.
+ sense_node), 1,
+ ssCoeff.header.sense_node);
+ }
+ break;
+
+ case CMD_GETFWVER:
+ size += (EXTERNAL_RELEASE_INFO_SIZE)*sizeof(u8);
+ break;
+
+ case CMD_FLASHUNLOCK:
+ res = flash_unlock();
+ if (res < OK)
+ pr_err("Impossible Unlock Flash ERROR %08X\n",
+ res);
+ else
+ pr_info("Flash Unlock OK!\n");
+ break;
+
+ case CMD_READFWFILE:
+ if (numberParam == 2) { /* read fw file */
+ pr_info("Reading FW File...\n");
+ res = readFwFile(info->board->fw_name, &fw,
+ funcToTest[1]);
+ if (res < OK)
+ pr_err("Error reading FW File ERROR %08X\n",
+ res);
+ else
+ pr_info("Read FW File Finished!\n");
+ kfree(fw.data);
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FLASHPROCEDURE:
+ if (numberParam == 3) { /* flashing procedure */
+ pr_info("Starting Flashing Procedure...\n");
+ res = flashProcedure(info->board->fw_name,
+ cmd[1],
+ cmd[2]);
+ if (res < OK)
+ pr_err("Error during flash procedure ERROR %08X\n",
+ res);
+ else
+ pr_info("Flash Procedure Finished!\n");
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FLASHERASEUNLOCK:
+ res = flash_erase_unlock();
+ if (res < OK)
+ pr_err("Error during flash erase unlock... ERROR %08X\n",
+ res);
+ else
+ pr_info("Flash Erase Unlock Finished!\n");
+ break;
+
+ case CMD_FLASHERASEPAGE:
+ if (numberParam == 2) { /* need to pass: keep_cx */
+ pr_info("Reading FW File...\n");
+ res = readFwFile(info->board->fw_name, &fw,
+ funcToTest[1]);
+ if (res < OK)
+ pr_err("Error reading FW File ERROR"
+ "%08X\n", res);
+ else
+ pr_info("Read FW File Finished!\n");
+ pr_info("Starting Flashing Page Erase...\n");
+ res = flash_erase_page_by_page(cmd[1], &fw);
+ if (res < OK)
+ pr_err("Error during flash page erase... ERROR %08X\n",
+ res);
+ else
+ pr_info("Flash Page Erase Finished!\n");
+ kfree(fw.data);
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ /*ITO TEST*/
+ case CMD_ITOTEST:
+ frameMS.node_data = NULL;
+ res = production_test_ito(limits_file, &tests,
+ &frameMS);
+
+ if (frameMS.node_data != NULL) {
+ size += (frameMS.node_data_size *
+ sizeof(short) + 2);
+ report = 1;
+ }
+ break;
+
+ /*Initialization*/
+ case CMD_INITTEST:
+ if (numberParam == 2)
+ res = production_test_initialization(cmd[1]);
+
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ case CMD_MSRAWTEST: /* MS Raw DATA TEST */
+ if (numberParam == 2) /* need to specify if stopOnFail
+ * */
+ res = production_test_ms_raw(limits_file,
+ cmd[1], &tests);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_MSINITDATATEST:/* MS CX DATA TEST */
+ if (numberParam == 2) /* need stopOnFail */
+ res = production_test_ms_cx(limits_file, cmd[1],
+ &tests);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SSRAWTEST: /* SS RAW DATA TEST */
+ if (numberParam == 2) /* need stopOnFail */
+ res = production_test_ss_raw(limits_file,
+ cmd[1], &tests);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SSINITDATATEST:/* SS IX CX DATA TEST */
+ if (numberParam == 2) /* need stopOnFail */
+ res = production_test_ss_ix_cx(limits_file,
+ cmd[1], &tests);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ /*PRODUCTION TEST*/
+ case CMD_MAINTEST:
+ if (numberParam >= 3) /* need to specify if stopOnFail
+ * saveInit and
+ * mpflag(optional)
+ */
+ if (numberParam == 3)
+ res = production_test_main(limits_file,
+ cmd[1],
+ cmd[2], &tests,
+ MP_FLAG_OTHERS);
+ else
+ res = production_test_main(limits_file,
+ cmd[1],
+ cmd[2], &tests,
+ cmd[3]);
+ else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FREELIMIT:
+ res = freeCurrentLimitsFile();
+ break;
+
+ case CMD_POWERCYCLE:
+ res = fts_chip_powercycle(info);
+ break;
+
+ case CMD_GETLIMITSFILE:
+ /* need to pass: path(optional) return error code +
+ * number of byte read otherwise GUI could not now how
+ * many byte read */
+ if (numberParam >= 1) {
+ lim.data = NULL;
+ lim.size = 0;
+ if (numberParam == 1)
+ res = getLimitsFile(limits_file, &lim);
+ else
+ res = getLimitsFile(path, &lim);
+ readData = lim.data;
+ fileSize = lim.size;
+ size += (fileSize * sizeof(u8));
+ if (byte_call == 1)
+ size += sizeof(u32); /* transmit as
+ * first 4 bytes
+ * the size */
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_GETLIMITSFILE_BYTE:
+ /* need to pass: byteToRead1 byteToRead0 */
+ if (numberParam >= 3) {
+ lim.data = NULL;
+ lim.size = 0;
+
+ u8ToU16_be(&cmd[1], &byteToRead);
+ addr = ((u64)byteToRead) * 4; /* number of
+ * words */
+
+ res = getLimitsFile(limits_file, &lim);
+
+ readData = lim.data;
+ fileSize = lim.size;
+
+ if (fileSize > addr) {
+ pr_err("Limits dimension expected by Host is less than actual size: expected = %d, real = %d\n",
+ byteToRead, fileSize);
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ size += (addr * sizeof(u8));
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_GETFWFILE:
+ /* need to pass: from (optional) otherwise select the
+ * approach chosen at compile time */
+ if (numberParam >= 1) {
+ if (numberParam == 1)
+ res = getFWdata(info->board->fw_name,
+ &readData, &fileSize);
+ else
+ res = getFWdata(path, &readData,
+ &fileSize);
+
+ size += (fileSize * sizeof(u8));
+ if (byte_call == 1)
+ size += sizeof(u32); /* transmit as
+ * first 4 bytes
+ * the size */
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_GETFWFILE_BYTE:
+ /* need to pass: byteToRead1 byteToRead0 */
+ if (numberParam == 3) {
+ u8ToU16_be(&cmd[1], &byteToRead);
+ addr = ((u64)byteToRead) * 4; /* number of
+ * words */
+ res = getFWdata(info->board->fw_name, &readData,
+ &fileSize);
+ if (fileSize > addr) {
+ pr_err("FW dimension expected by Host is less than actual size: expected = %d, real = %d\n",
+ byteToRead, fileSize);
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ size += (addr * sizeof(u8)); /* return always
+ * the amount
+ * requested by
+ * host, if real
+ * size is
+ * smaller, the
+ * data are
+ * padded to
+ * zero */
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ /* finish all the diagnostic command with a goto ERROR in order
+ * to skip the modification on driver_test_buff
+ * remember to set properly the limit and printed variables in
+ * order to make the seq_file logic to work */
+ case CMD_DIAGNOSTIC:
+ index = 0;
+ size = 0;
+ fileSize = 256 * 1024 * sizeof(char);
+ driver_test_buff = (u8 *)kzalloc(fileSize, GFP_KERNEL);
+ readData = (u8 *)kmalloc((ERROR_DUMP_ROW_SIZE *
+ ERROR_DUMP_COL_SIZE) *
+ sizeof(u8), GFP_KERNEL);
+ if (driver_test_buff == NULL || readData == NULL) {
+ res = ERROR_ALLOC;
+ pr_err("Impossible allocate memory for buffers! ERROR %08X!\n",
+ res);
+ goto END;
+ }
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "DIAGNOSTIC TEST:\n1) I2C Test: ");
+ index += j;
+
+ res = fts_writeReadU8UX(FTS_CMD_HW_REG_R,
+ ADDR_SIZE_HW_REG, ADDR_DCHIP_ID,
+ (u8 *)&temp, 2,
+ DUMMY_HW_REG);
+ if (res < OK) {
+ pr_err("Error during I2C test: ERROR %08X!\n",
+ res);
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index, "ERROR %08X\n",
+ res);
+ index += j;
+ res = ERROR_OP_NOT_ALLOW;
+ goto END_DIAGNOSTIC;
+ }
+
+ temp &= 0xFFFF;
+ pr_info("Chip ID = %04X!\n", temp);
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "DATA = %04X, expected = %02X%02X\n",
+ temp, DCHIP_ID_1,
+ DCHIP_ID_0);
+ index += j;
+ if (temp != ((DCHIP_ID_1 << 8) | DCHIP_ID_0)) {
+ pr_err("Wrong CHIP ID, Diagnostic failed!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ goto END_DIAGNOSTIC;
+ }
+
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "Present Driver Mode: %08X\n",
+ info->mode);
+ index += j;
+
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "2) FW running: Sensing On...");
+ index += j;
+ pr_info("Sensing On!\n");
+ readData[0] = FTS_CMD_SCAN_MODE;
+ readData[1] = SCAN_MODE_ACTIVE;
+ readData[2] = 0x1;
+ fts_write_heap(readData, 3);
+ res = checkEcho(readData, 3);
+ if (res < OK) {
+ pr_err("No Echo received.. ERROR %08X !\n",
+ res);
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "No echo found... ERROR %08X!\n",
+ res);
+ index += j;
+ goto END_DIAGNOSTIC;
+ } else {
+ pr_info("Echo FOUND... OK!\n");
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "Echo FOUND... OK!\n");
+ index += j;
+ }
+
+ pr_info("Reading Frames...!\n");
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "3) Read Frames:\n");
+ index += j;
+ for (temp = 0; temp < DIAGNOSTIC_NUM_FRAME; temp++) {
+ pr_info("Iteration n. %d...\n", temp + 1);
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "Iteration n. %d...\n",
+ temp + 1);
+ index += j;
+ for (addr = 0; addr < 3; addr++) {
+ switch (addr) {
+ case 0:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "MS RAW FRAME =");
+ index += j;
+ res |= getMSFrame3(MS_RAW,
+ &frameMS);
+ break;
+ case 2:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "MS STRENGTH FRAME =");
+ index += j;
+ res |= getMSFrame3(MS_STRENGTH,
+ &frameMS);
+ break;
+ case 1:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "MS BASELINE FRAME =");
+ index += j;
+ res |= getMSFrame3(MS_BASELINE,
+ &frameMS);
+ break;
+ }
+ if (res < OK) {
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "No data! ERROR %08X\n",
+ res);
+ index += j;
+ } else {
+ for (address = 0; address <
+ frameMS.node_data_size;
+ address++) {
+ if (address %
+ frameMS.header.
+ sense_node == 0) {
+ j = scnprintf(
+ &driver_test_buff
+ [index],
+ fileSize -
+ index,
+ "\n");
+ index += j;
+ }
+ j = scnprintf(
+ &driver_test_buff
+ [index],
+ fileSize - index,
+ "%5d, ",
+ frameMS.
+ node_data[address]);
+ index += j;
+ }
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index, "\n");
+ index += j;
+ }
+ if (frameMS.node_data != NULL)
+ kfree(frameMS.node_data);
+ }
+ for (addr = 0; addr < 3; addr++) {
+ switch (addr) {
+ case 0:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "SS RAW FRAME =\n");
+ index += j;
+ res |= getSSFrame3(SS_RAW,
+ &frameSS);
+ break;
+ case 2:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "SS STRENGTH FRAME =\n");
+ index += j;
+ res |= getSSFrame3(SS_STRENGTH,
+ &frameSS);
+ break;
+ case 1:
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "SS BASELINE FRAME =\n");
+ index += j;
+ res |= getSSFrame3(SS_BASELINE,
+ &frameSS);
+ break;
+ }
+ if (res < OK) {
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "No data! ERROR %08X\n",
+ res);
+ index += j;
+ } else {
+ int num;
+ short *data;
+
+ num = frameSS.header.force_node;
+ data = frameSS.force_data;
+ for (address = 0;
+ address < num;
+ address++) {
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "%d\n",
+ data[address]);
+ index += j;
+ }
+
+ num = frameSS.header.sense_node;
+ data = frameSS.sense_data;
+ for (address = 0;
+ address < num;
+ address++) {
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "%d, ",
+ data[address]);
+ index += j;
+ }
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index, "\n");
+ index += j;
+ }
+ if (frameSS.force_data != NULL)
+ kfree(frameSS.force_data);
+ if (frameSS.sense_data != NULL)
+ kfree(frameSS.sense_data);
+ }
+ }
+
+
+ pr_info("Reading error info...\n");
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "4) FW INFO DUMP: ");
+ index += j;
+ temp = dumpErrorInfo(readData, ERROR_DUMP_ROW_SIZE *
+ ERROR_DUMP_COL_SIZE);
+ /* OR to detect if there are failures also in the
+ * previous reading of frames and write the correct
+ * result */
+ if (temp < OK) {
+ pr_err("Error during dump: ERROR %08X!\n",
+ res);
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index, "ERROR %08X\n",
+ temp);
+ index += j;
+ } else {
+ pr_info("DUMP OK!\n");
+ for (temp = 0; temp < ERROR_DUMP_ROW_SIZE *
+ ERROR_DUMP_COL_SIZE; temp++) {
+ if (temp % ERROR_DUMP_COL_SIZE == 0) {
+ j = scnprintf(
+ &driver_test_buff[index],
+ fileSize - index,
+ "\n%2d - ",
+ temp / ERROR_DUMP_COL_SIZE);
+ index += j;
+ }
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index, "%02X ",
+ readData[temp]);
+ index += j;
+ }
+ }
+ res |= temp;
+
+END_DIAGNOSTIC:
+ if (res < OK) {
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "\nRESULT = FAIL\n");
+ index += j;
+ } else {
+ j = scnprintf(&driver_test_buff[index],
+ fileSize - index,
+ "\nRESULT = FINISHED\n");
+ index += j;
+ }
+ /* the sting is already terminated with the null char by
+ * scnprintf */
+ limit = index;
+ printed = 0;
+ goto ERROR;
+ break;
+
+#ifdef I2C_INTERFACE
+ case CMD_CHANGE_SAD:
+ res = changeSAD(cmd[1]);
+ break;
+#endif
+
+ case CMD_TRIGGER_FORCECAL:
+ cmd[0] = CAL_MS_TOUCH | CAL_SS_TOUCH;
+ cmd[1] = 0x00;
+ fts_enableInterrupt(false);
+ res = writeSysCmd(SYS_CMD_FORCE_CAL, cmd, 2);
+ res |= fts_enableInterrupt(true);
+ if (res < OK)
+ pr_err("can not trigger Force Cal! ERROR %08X\n",
+ res);
+ else
+ pr_info("MS and SS force cal triggered!\n");
+ break;
+
+ case CMD_BASELINE_ADAPTATION:
+ /* need to pass: enable */
+ if (numberParam == 2) {
+ if (cmd[1] == 0x01)
+ pr_info("Enabling Baseline adaptation...\n");
+ else {
+ pr_info("Disabling Baseline adaptation...\n");
+ cmd[1] = 0x00; /* set to zero to
+ * disable baseline
+ * adaptation */
+ }
+
+ res = writeConfig(ADDR_CONFIG_AUTOCAL, &cmd[1],
+ 1);
+ if (res < OK)
+ pr_err("Baseline adaptation operation FAILED! ERROR %08X\n",
+ res);
+ else
+ pr_info("Baseline adaptation operation OK!\n");
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FREQ_HOP:
+ /* need to pass: enable */
+ if (numberParam == 2) {
+ pr_info("Reading MNM register...\n");
+ res = readConfig(ADDR_CONFIG_MNM, &cmd[2], 1);
+ if (res < OK) {
+ pr_err("Reading MNM register... ERROR %08X!\n",
+ res);
+ break;
+ }
+
+ if (cmd[1] == 0x01) {
+ pr_info("Enabling Frequency Hopping... %02X => %02X\n",
+ cmd[2], cmd[2] | 0x01);
+ cmd[2] |= 0x01; /* set bit 0 to enable
+ * Frequency Hopping */
+ } else {
+ pr_info("Disabling Frequency Hopping... %02X => %02X\n",
+ cmd[2], cmd[2] & (~0x01));
+ cmd[2] &= (~0x01); /* reset bit 0
+ * to disable
+ * Frequency
+ * Hopping */
+ }
+
+ res = writeConfig(ADDR_CONFIG_MNM, &cmd[2], 1);
+ if (res < OK)
+ pr_err("Frequency Hopping operation FAILED! ERROR %08X\n",
+ res);
+ else
+ pr_info("Frequency Hopping operation OK!\n");
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_READ_SYNC_FRAME:
+ /* need to pass: frameType (this parameter can be
+ * LOAD_SYNC_FRAME_STRENGTH or LOAD_SYNC_FRAME_BASELINE)
+ * */
+ if (numberParam == 2) {
+ pr_info("Reading Sync Frame...\n");
+ res = getSyncFrame(cmd[1], &frameMS, &frameSS);
+ if (res < OK)
+ pr_err("Error while taking the Sync Frame frame... ERROR %08X\n",
+ res);
+
+ else {
+ pr_info("The total frames size is %d words\n",
+ res);
+ size += (res * sizeof(short) + 4);
+ /* +4 to add force and sense channels
+ * for MS and SS
+ * set res to OK because if getSyncFrame
+ * 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);
+ 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);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_SET_OPERATING_FREQ:
+ /* need to pass: freq3 freq2 freq1 freq0 */
+ if (numberParam == 5) {
+ res = fts_enableInterrupt(false);
+ if (res >= OK) {
+ pr_info("Setting Scan Freq...\n");
+ u8ToU32_be(&cmd[1], &fileSize);
+ /* fileSize is used just as container
+ * variable, sorry for the name! */
+
+ res = setActiveScanFrequency(fileSize);
+ if (res < OK)
+ pr_err("Error while setting the scan frequency... ERROR %08X\n",
+ res);
+ else {
+ /* setActiveScan Frequency leave
+ * the chip in reset state but
+ * with the new scan freq set */
+ /* need to enable the scan mode
+ * and re-enable the interrupts
+ * */
+ res |= setScanMode(
+ SCAN_MODE_LOCKED,
+ LOCKED_ACTIVE);
+ /* this is a choice to force
+ * the IC to use the freq set */
+ res |= fts_enableInterrupt(
+ true);
+ pr_info("Setting Scan Freq... res = %08X\n",
+ res);
+ }
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ break;
+
+ case CMD_TP_SENS_MODE:
+ /* need to pass: enter (optional)saveGain */
+ if (numberParam >= 2) {
+ if (numberParam == 2)
+ cmd[2] = 0; /* by default never save
+ * the gain (used only
+ * when exit) */
+
+ res = tp_sensitivity_mode(cmd[1], cmd[2]);
+ if (res < OK)
+ pr_err("Error while setting TP Sens mode... ERROR %08X\n",
+ res);
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ case CMD_TP_SENS_SET_SCAN_MODE:
+ /* need to pass: scan_type, enableGains */
+ if (numberParam == 3) {
+ if (cmd[1] == 0x07) { /* To match the api for
+ * C2/F2
+ */
+ res = tp_sensitivity_set_scan_mode(
+ LOCKED_SINGLE_ENDED_ONLY_MUTUAL_0,
+ cmd[2]);
+ /* this force the IC to lock in a scan
+ * mode
+ */
+ if (res < OK)
+ pr_err("Error while setting TP Sens scan mode... ERROR %08X\n",
+ res);
+ } else {
+ pr_err("Wrong parameter!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+ break;
+
+ case CMD_TP_SENS_PRECAL_SS:
+ /* need to pass: target1 target0 percentage(optional) */
+ if (numberParam >= 3) {
+ if (numberParam > 3)
+ temp = cmd[3];
+ else
+ temp = SENS_TEST_PERC_TARGET_PRECAL;
+
+ pr_info("Setting target = %d and percentage = %d\n",
+ (cmd[1] << 8 | cmd[2]), temp);
+
+ res = tp_sensitivity_test_pre_cal_ss(&frameSS,
+ (cmd[1] << 8 |
+ cmd[2]),
+ temp);
+ if (res < OK)
+ pr_err("Error while setting the scan frequency... ERROR %08X\n",
+ res);
+
+ if ((frameSS.force_data != NULL) &&
+ (frameSS.sense_data != NULL)) {
+ size += ((frameSS.header.force_node +
+ frameSS.header.sense_node) *
+ sizeof(short) + 2);
+ /*make error code positive to print the
+ * frame*/
+ res &= (~0x80000000);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_TP_SENS_PRECAL_MS:
+ /* need to pass: target1 target0 calibrate
+ * percentage(optional) */
+ if (numberParam >= 4) {
+ if (numberParam > 4)
+ temp = cmd[4];
+ else
+ temp = SENS_TEST_PERC_TARGET_PRECAL;
+
+ pr_info("Setting target = %d and percentage = %d\n",
+ (cmd[1] << 8 | cmd[2]), temp);
+
+ res = tp_sensitivity_test_pre_cal_ms(&frameMS,
+ (cmd[1] << 8 |
+ cmd[2]),
+ temp);
+ if (res < OK)
+ pr_err("Error during TP Sensitivity Precal ... ERROR %08X\n",
+ res);
+
+ if (cmd[3] != 0) {
+ pr_info("Computing gains with target = %d and saveGain = %d\n",
+ (cmd[1] << 8 | cmd[2]), 0);
+ temp = tp_sensitivity_compute_gains(
+ &frameMS, (cmd[1] << 8 |
+ cmd[2]),
+ 0);
+ if (temp < OK)
+ pr_err("Error during TP Sensitivity Calibration... ERROR %08X\n",
+ temp);
+ res |= temp;
+ }
+
+ if (frameMS.node_data != NULL) {
+ size += (frameMS.node_data_size *
+ sizeof(short) + 2);
+ /*make error code positive to print the
+ * frame*/
+ res &= (~0x80000000);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_TP_SENS_POSTCAL_MS:
+ /* need to pass: target1 target0 executeTest
+ * percentage(optional) */
+ if (numberParam >= 4) {
+ if (cmd[3] != 0) {
+ if (numberParam > 4)
+ temp = cmd[4];
+ else
+ temp =
+ SENS_TEST_PERC_TARGET_POSTCAL;
+ } else
+ temp = -1;
+
+ pr_info("Setting target = %d and percentage = %d\n",
+ (cmd[1] << 8 | cmd[2]), temp);
+
+ res = tp_sensitivity_test_post_cal_ms(&frameMS,
+ &deltas,
+ (cmd[1] << 8 |
+ cmd[2]),
+ temp,
+ &meanNorm,
+ &meanEdge);
+ if (res < OK)
+ pr_err("Error during TP Sensitivity Post Cal ... ERROR %08X\n",
+ res);
+
+ /* processing for a proper printing on the shell
+ * */
+ if ((frameMS.node_data != NULL) &&
+ (deltas.node_data != NULL)) {
+ size += ((frameMS.node_data_size +
+ deltas.node_data_size) *
+ sizeof(short) +
+ 2 + 8);/* +2 force and
+ * sense len, +8
+ * mean_normal/edge
+ * */
+ /*make error code positive to print the
+ * frame*/
+ res &= (~0x80000000);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+
+ case CMD_TP_SENS_STD:
+ /* need to pass: numFrames */
+ if (numberParam >= 2) {
+ res = tp_sensitivity_test_std_ms(cmd[1],
+ &frameMS);
+ if (res < OK)
+ pr_err("Error during TP Sensitivity STD... ERROR %08X\n",
+ res);
+
+ /* processing for a proper printing on the shell
+ * */
+ if (frameMS.node_data != NULL) {
+ size += ((frameMS.node_data_size) *
+ sizeof(short) + 2);
+ /* +2 force and sense len */
+ /*make error code positive to print the
+ * frame*/
+ res &= (~0x80000000);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_FORCE_TOUCH_ACTIVE:
+ /* Single parameter indicates force touch state */
+ if (numberParam == 2) {
+ if (cmd[1] > 1) {
+ pr_err("Parameter should be 1 or 0\n");
+ res = ERROR_OP_NOT_ALLOW;
+ } else {
+ pr_info("FTS_BUS_REF_FORCE_ACTIVE: %s\n",
+ cmd[1] ? "ON" : "OFF");
+ fts_set_bus_ref(info,
+ FTS_BUS_REF_FORCE_ACTIVE,
+ cmd[1]);
+ res = OK;
+ if (cmd[1])
+ __pm_stay_awake(info->wakesrc);
+ else
+ __pm_relax(info->wakesrc);
+ }
+ } else {
+ pr_err("Wrong number of parameters!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+ break;
+
+ case CMD_INFOBLOCK_STATUS:
+ res = fts_system_reset();
+ if (res >= OK) {
+ res = pollForErrorType(error_to_search, 2);
+ if (res < OK) {
+ pr_err("No info block corruption!\n");
+ res = OK;
+ } else {
+ pr_info("Info block errors found!\n");
+ res = ERROR_INFO_BLOCK;
+ }
+ }
+ break;
+
+ default:
+ pr_err("COMMAND ID NOT VALID!!!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ break;
+ }
+
+ /* res2 = fts_enableInterrupt(true);
+ * the interrupt was disabled on purpose in this node because it
+ * can be used for testing procedure and between one step and
+ * another the interrupt wan to be kept disabled
+ * if (res2 < 0) {
+ * pr_err("stm_driver_test_show: ERROR %08X\n",
+ * (res2 | ERROR_ENABLE_INTER));
+ * }*/
+ } else {
+ pr_err("NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n");
+ res = ERROR_OP_NOT_ALLOW;
+ }
+
+END: /* here start the reporting phase, assembling the data to send in the
+ * file node */
+ if (driver_test_buff != NULL) {
+ pr_info("Consecutive echo on the file node, free the buffer with the previous result\n");
+ kfree(driver_test_buff);
+ }
+
+ if (byte_call == 0) {
+ size *= 2;
+ size += 2; /* add \n and \0 (terminator char) */
+ } else {
+ if (bin_output != 1) {
+ size *= 2; /* need to code each byte as HEX string */
+ size -= 1; /* start byte is just one, the extra
+ * byte of end byte taken by \n */
+ } else
+ size += 1; /* add \n */
+ }
+
+ pr_info("Size = %d\n", size);
+ driver_test_buff = (u8 *)kzalloc(size, GFP_KERNEL);
+ pr_info("Finish to allocate memory!\n");
+ if (driver_test_buff == NULL) {
+ pr_err("Unable to allocate driver_test_buff! ERROR %08X\n",
+ ERROR_ALLOC);
+ goto ERROR;
+ }
+
+ if (byte_call == 0) {
+ index = 0;
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "{ ");
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%08X", res);
+ if (res >= OK || report) {
+ /*all the other cases are already fine printing only the
+ * res.*/
+ switch (funcToTest[0]) {
+ case CMD_VERSION:
+ case CMD_READ:
+ case CMD_WRITEREAD:
+ case CMD_WRITETHENWRITEREAD:
+ case CMD_WRITEREADU8UX:
+ case CMD_WRITEU8UXTHENWRITEREADU8UX:
+ case CMD_READCONFIG:
+ case CMD_POLLFOREVENT:
+ /* pr_err("Data = "); */
+ if (mess.dummy == 1)
+ j = 1;
+ else
+ j = 0;
+ for (; j < byteToRead + mess.dummy; j++) {
+ /* pr_err("%02X ", readData[j]); */
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X", readData[j]);
+ /* this approach is much more faster */
+ }
+ /* pr_err("\n"); */
+ break;
+ case CMD_GETFWFILE:
+ case CMD_GETLIMITSFILE:
+ pr_info("Start To parse!\n");
+ for (j = 0; j < fileSize; j++) {
+ /* pr_err("%02X ", readData[j]); */
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X", readData[j]);
+ }
+ pr_info("Finish to parse!\n");
+ break;
+ case CMD_GETFORCELEN:
+ case CMD_GETSENSELEN:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)temp);
+ break;
+ case CMD_GETMSFRAME:
+ case CMD_TP_SENS_PRECAL_MS:
+ case CMD_TP_SENS_POSTCAL_MS:
+ case CMD_TP_SENS_STD:
+ case CMD_ITOTEST:
+
+ if (frameMS.node_data == NULL)
+ break;
+
+ if (res != OK)
+ driver_test_buff[2] = '8';
+ /* convert back error code to negative */
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.sense_node);
+
+ for (j = 0; j < frameMS.node_data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameMS.node_data[j] & 0xFF00) >> 8,
+ frameMS.node_data[j] & 0xFF);
+ }
+
+ kfree(frameMS.node_data);
+
+ if (funcToTest[0] == CMD_TP_SENS_POSTCAL_MS) {
+ /* print also mean and deltas */
+ for (j = 0; j < deltas.node_data_size;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (deltas.node_data[j] &
+ 0xFF00) >> 8,
+ deltas.node_data[j] &
+ 0xFF);
+ }
+ kfree(deltas.node_data);
+
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%08X", meanNorm);
+
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%08X", meanEdge);
+ }
+ break;
+ case CMD_GETSSFRAME:
+ case CMD_TP_SENS_PRECAL_SS:
+ if (res != OK)
+ driver_test_buff[2] = '8';
+ /* convert back error code to negative */
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.force_node);
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.sense_node);
+ /* Copying self raw data Force */
+ for (j = 0; j < frameSS.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.force_data[j] & 0xFF00) >> 8,
+ frameSS.force_data[j] & 0xFF);
+ }
+
+ /* Copying self raw data Sense */
+ for (j = 0; j < frameSS.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.sense_data[j] & 0xFF00) >> 8,
+ frameSS.sense_data[j] & 0xFF);
+ }
+
+ kfree(frameSS.force_data);
+ kfree(frameSS.sense_data);
+ break;
+
+ case CMD_GETSYNCFRAME:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.sense_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.sense_node);
+
+ /* Copying mutual data */
+ for (j = 0; j < frameMS.node_data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameMS.node_data[j] &
+ 0xFF00) >> 8,
+ frameMS.node_data[j] & 0xFF);
+ }
+
+ /* Copying self data Force */
+ for (j = 0; j < frameSS.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.force_data[j] &
+ 0xFF00) >> 8,
+ frameSS.force_data[j] & 0xFF);
+ }
+
+ /* Copying self data Sense */
+ for (j = 0; j < frameSS.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.sense_data[j] &
+ 0xFF00) >> 8,
+ frameSS.sense_data[j] & 0xFF);
+ }
+
+ kfree(frameMS.node_data);
+ kfree(frameSS.force_data);
+ kfree(frameSS.sense_data);
+ break;
+
+ case CMD_READMSCOMPDATA:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)compData.header.type);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)compData.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)compData.header.sense_node);
+
+ /* Cpying CX1 value */
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ compData.cx1 & 0xFF);
+
+ /* Copying CX2 values */
+ for (j = 0; j < compData.node_data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ compData.node_data[j] & 0xFF);
+ }
+
+ kfree(compData.node_data);
+ break;
+
+ case CMD_READSSCOMPDATA:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)comData.header.type);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.header.sense_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.f_ix1 & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.s_ix1 & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.f_cx1 & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.s_cx1 & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.f_ix0 & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ comData.s_ix0 & 0xFF);
+
+ /* Copying IX2 Force */
+ for (j = 0; j < comData.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ comData.ix2_fm[j] & 0xFF);
+ }
+
+ /* Copying IX2 Sense */
+ for (j = 0; j < comData.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ comData.ix2_sn[j] & 0xFF);
+ }
+
+ /* Copying CX2 Force */
+ for (j = 0; j < comData.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ comData.cx2_fm[j] & 0xFF);
+ }
+
+ /* Copying CX2 Sense */
+ for (j = 0; j < comData.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ comData.cx2_sn[j] & 0xFF);
+ }
+
+ kfree(comData.ix2_fm);
+ kfree(comData.ix2_sn);
+ kfree(comData.cx2_fm);
+ kfree(comData.cx2_sn);
+ break;
+
+
+ case CMD_READGOLDENMUTUAL:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)gmRawData.hdm_hdr.type);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ gmRawData.hdr.ms_f_len);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ gmRawData.hdr.ms_s_len);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ gmRawData.hdr.ss_f_len);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ gmRawData.hdr.ss_s_len);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ gmRawData.hdr.ms_k_len);
+
+ /* Copying Golden Mutual raw values */
+ for (j = 0; j < gmRawData.data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index, "%04X",
+ (u16)gmRawData.data[j]);
+ }
+
+ kfree(gmRawData.data);
+ break;
+
+ case CMD_READTOTMSCOMPDATA:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)totCompData.header.type);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)totCompData.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)totCompData.header.sense_node);
+
+ /* Copying TOT CX values */
+ for (j = 0; j < totCompData.node_data_size;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (totCompData.node_data[j] &
+ 0xFF00) >> 8,
+ totCompData.node_data[j] &
+ 0xFF);
+ }
+
+ kfree(totCompData.node_data);
+ break;
+
+ case CMD_READTOTSSCOMPDATA:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)totComData.header.type);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ totComData.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ totComData.header.sense_node);
+
+ /* Copying TOT IX Force */
+ for (j = 0; j < totComData.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (totComData.ix_fm[j] &
+ 0xFF00) >> 8,
+ totComData.ix_fm[j] &
+ 0xFF);
+ }
+
+ /* Copying TOT IX Sense */
+ for (j = 0; j < totComData.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (totComData.ix_sn[j] &
+ 0xFF00) >> 8,
+ totComData.ix_sn[j] &
+ 0xFF);
+ }
+
+ /* Copying TOT CX Force */
+ for (j = 0; j < totComData.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (totComData.cx_fm[j] &
+ 0xFF00) >> 8,
+ totComData.cx_fm[j] &
+ 0xFF);
+ }
+
+ /* Copying CX2 Sense */
+ for (j = 0; j < totComData.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (totComData.cx_sn[j] &
+ 0xFF00) >> 8,
+ totComData.cx_sn[j] &
+ 0xFF);
+ }
+
+ kfree(totComData.ix_fm);
+ kfree(totComData.ix_sn);
+ kfree(totComData.cx_fm);
+ kfree(totComData.cx_sn);
+ break;
+
+ case CMD_READSENSCOEFF:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)msCoeff.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)msCoeff.header.sense_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)ssCoeff.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)ssCoeff.header.sense_node);
+
+ /* Copying MS Coefficients */
+ for (j = 0; j < msCoeff.node_data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ msCoeff.ms_coeff[j] & 0xFF);
+ }
+
+ /* Copying SS force Coefficients */
+ for (j = 0; j < ssCoeff.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ ssCoeff.ss_force_coeff[j] &
+ 0xFF);
+ }
+
+ /* Copying SS sense Coefficients */
+ for (j = 0; j < ssCoeff.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ ssCoeff.ss_sense_coeff[j] &
+ 0xFF);
+ }
+
+ kfree(msCoeff.ms_coeff);
+ kfree(ssCoeff.ss_force_coeff);
+ kfree(ssCoeff.ss_sense_coeff);
+ break;
+
+ case CMD_GETFWVER:
+ for (j = 0; j < EXTERNAL_RELEASE_INFO_SIZE;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ systemInfo.u8_releaseInfo[j]);
+ }
+ break;
+
+ case CMD_READCOMPDATAHEAD:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ dataHead.type);
+ break;
+
+ case CMD_READ_SYNC_FRAME:
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameMS.header.sense_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.force_node);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%02X",
+ (u8)frameSS.header.sense_node);
+
+ /* Copying mutual data */
+ for (j = 0; j < frameMS.node_data_size; j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameMS.node_data[j] &
+ 0xFF00) >> 8,
+ frameMS.node_data[j] &
+ 0xFF);
+ }
+
+ /* Copying self data Force */
+ for (j = 0; j < frameSS.header.force_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.force_data[j] &
+ 0xFF00) >> 8,
+ frameSS.force_data[j] &
+ 0xFF);
+ }
+
+
+ /* Copying self data Sense */
+ for (j = 0; j < frameSS.header.sense_node;
+ j++) {
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (frameSS.sense_data[j] &
+ 0xFF00) >> 8,
+ frameSS.sense_data[j] &
+ 0xFF);
+ }
+
+ kfree(frameMS.node_data);
+ kfree(frameSS.force_data);
+ kfree(frameSS.sense_data);
+ break;
+
+
+ default:
+ break;
+ }
+ }
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, " }\n");
+ limit = size - 1;/* avoid to print \0 in the shell */
+ printed = 0;
+ } else {
+ /* start byte */
+ driver_test_buff[index++] = MESSAGE_START_BYTE;
+ if (bin_output == 1) {
+ /* msg_size */
+ driver_test_buff[index++] = (size & 0xFF00) >> 8;
+ driver_test_buff[index++] = (size & 0x00FF);
+ /* counter id */
+ driver_test_buff[index++] =
+ (mess.counter & 0xFF00) >> 8;
+ driver_test_buff[index++] = (mess.counter & 0x00FF);
+ /* action */
+ driver_test_buff[index++] = (mess.action & 0xFF00) >> 8;
+ driver_test_buff[index++] = (mess.action & 0x00FF);
+ /* error */
+ driver_test_buff[index++] = (res & 0xFF00) >> 8;
+ driver_test_buff[index++] = (res & 0x00FF);
+ } else {
+ if (funcToTest[0] == CMD_GETLIMITSFILE_BYTE ||
+ funcToTest[0] == CMD_GETFWFILE_BYTE)
+ index += scnprintf(&driver_test_buff[index],
+ size - index,
+ "%02X%02X",
+ (((fileSize + 3) / 4) & 0xFF00) >> 8,
+ ((fileSize + 3) / 4) & 0x00FF);
+ else
+ index += scnprintf(&driver_test_buff[index],
+ size - index,
+ "%02X%02X", (size & 0xFF00) >> 8,
+ size & 0xFF);
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%04X",
+ (u16)mess.counter);
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%04X",
+ (u16)mess.action);
+ index += scnprintf(&driver_test_buff[index],
+ size - index,
+ "%02X%02X", (res & 0xFF00) >> 8,
+ res & 0xFF);
+ }
+
+ switch (funcToTest[0]) {
+ case CMD_VERSION_BYTE:
+ case CMD_READ_BYTE:
+ case CMD_WRITEREAD_BYTE:
+ case CMD_WRITETHENWRITEREAD_BYTE:
+ case CMD_WRITEREADU8UX_BYTE:
+ case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE:
+ if (bin_output == 1) {
+ if (mess.dummy == 1)
+ memcpy(&driver_test_buff[index],
+ &readData[1], byteToRead);
+ else
+ memcpy(&driver_test_buff[index],
+ readData, byteToRead);
+ index += byteToRead;
+ } else {
+ j = mess.dummy;
+ for (; j < byteToRead + mess.dummy; j++)
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ (u8)readData[j]);
+ }
+ break;
+
+ case CMD_GETLIMITSFILE_BYTE:
+ case CMD_GETFWFILE_BYTE:
+ if (bin_output == 1) {
+ /* override the msg_size with dimension in words
+ * */
+ driver_test_buff[1] = (
+ ((fileSize + 3) / 4) & 0xFF00) >> 8;
+ driver_test_buff[2] = (
+ ((fileSize + 3) / 4) & 0x00FF);
+
+ if (readData != NULL)
+ memcpy(&driver_test_buff[index],
+ readData, fileSize);
+ else
+ pr_err("readData = NULL... returning junk data!");
+ index += addr; /* in this case the byte to read
+ * are stored in addr because it
+ * is a u64 end byte need to be
+ * inserted at the end of the
+ * padded memory */
+ } else {
+ /* snprintf(&driver_test_buff[1], 3, "%02X",
+ * (((fileSize + 3) / 4)&0xFF00) >> 8); */
+ /* snprintf(&driver_test_buff[3], 3, "%02X",
+ * ((fileSize + 3) / 4)&0x00FF); */
+ for (j = 0; j < fileSize; j++)
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X",
+ (u8)readData[j]);
+ for (; j < addr; j++)
+ index += scnprintf(
+ &driver_test_buff[index],
+ size - index,
+ "%02X", 0); /* pad memory
+ * with 0x00 */
+ }
+ break;
+ default:
+ break;
+ }
+
+ index += scnprintf(&driver_test_buff[index],
+ size - index, "%c\n", MESSAGE_END_BYTE);
+ /*for(j=0; j<size; j++){
+ * pr_err("%c", driver_test_buff[j]);
+ * }*/
+ limit = size;
+ printed = 0;
+ }
+ERROR:
+ 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 */
+
+ /* pr_err(0,"numberParameters = %d\n", numberParam); */
+
+ kfree(readData);
+ kfree(cmd);
+ kfree(funcToTest);
+ kfree(pbuf);
+
+ fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
+
+exit:
+ return count;
+}
+
+/** @}*/
+
+/**
+ * file_operations struct which define the functions for the canonical
+ * operation on a device file node (open. read, write etc.)
+ */
+static struct file_operations fts_driver_test_ops = {
+ .open = fts_driver_test_open,
+ .read = seq_read,
+ .write = fts_driver_test_write,
+ .llseek = seq_lseek,
+ .release = fts_driver_test_release
+};
+
+/*****************************************************************************/
+
+/**
+ * This function is called in the probe to initialize and create the directory
+ * proc/fts and the driver test file node DRIVER_TEST_FILE_NODE into the /proc
+ * file system
+ * @return OK if success or an error code which specify the type of error
+ */
+int fts_proc_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ int retval = 0;
+
+
+ fts_dir = proc_mkdir_data("fts", 0777, NULL, NULL);
+ if (fts_dir == NULL) { /* directory creation failed */
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ entry = proc_create(DRIVER_TEST_FILE_NODE, 0777, fts_dir,
+ &fts_driver_test_ops);
+
+ if (entry)
+ pr_info("%s: proc entry CREATED!\n", __func__);
+ else {
+ pr_err("%s: error creating proc entry!\n", __func__);
+ retval = -ENOMEM;
+ goto badfile;
+ }
+ return OK;
+badfile:
+ remove_proc_entry("fts", NULL);
+out:
+ return retval;
+}
+
+/**
+ * Delete and Clean from the file system, all the references to the driver test
+ * file node
+ * @return OK
+ */
+int fts_proc_remove(void)
+{
+ remove_proc_entry(DRIVER_TEST_FILE_NODE, fts_dir);
+ remove_proc_entry("fts", NULL);
+ return OK;
+}