| /** |
| * 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> |
| |
| #include <linux/notifier.h> |
| #include <linux/msm_drm_notify.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 int fts_chip_initialization(struct fts_ts_info *info, int init_type); |
| |
| /** |
| * 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; |
| #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, |
| ®, 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); |
| |
| /* /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, |
| NULL, |
| }; |
| |
| /** @}*/ |
| /** @}*/ |
| |
| |
|