| /** |
| * 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/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/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 "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" |
| |
| |
| /* Switch GPIO values */ |
| #define FTS_SWITCH_GPIO_VALUE_SLPI_MASTER 0 |
| #define FTS_SWITCH_GPIO_VALUE_AP_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 typeOfComand[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 |
| |
| extern spinlock_t fts_int; |
| |
| 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); |
| |
| static void fts_report_timestamp(struct fts_ts_info *info) |
| { |
| input_event(info->input_dev, EV_MSC, MSC_TIMESTAMP, |
| info->timestamp / 1000); |
| } |
| |
| /** |
| * 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); |
| fts_report_timestamp(info); |
| 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]; |
| 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 */ |
| sscanf(buf, "%100s %d %d", path, &mode[0], &mode[1]); |
| pr_info("fts_fwupdate_store: path = %s\n", path); |
| |
| 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); |
| |
| 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) |
| { |
| int error; |
| char temp[100]; |
| |
| error = scnprintf(buf, |
| PAGE_SIZE, |
| "%s\n", |
| printHex("ST-V", |
| systemInfo.u8_releaseInfo, |
| EXTERNAL_RELEASE_INFO_SIZE, |
| temp, |
| sizeof(temp))); |
| |
| return error; |
| } |
| |
| /** |
| * 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_disableInterrupt(); |
| 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(); |
| 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; |
| 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 { |
| sscanf(p, "%02X ", &temp); |
| p += 3; |
| res = check_feature_feasibility(info, temp); |
| if (res >= OK) { |
| switch (temp) { |
| #ifdef GESTURE_MODE |
| case FEAT_SEL_GESTURE: |
| sscanf(p, "%02X ", &info->gesture_enabled); |
| pr_info("fts_feature_enable: Gesture Enabled = %d\n", |
| info->gesture_enabled); |
| break; |
| #endif |
| |
| #ifdef GLOVE_MODE |
| case FEAT_SEL_GLOVE: |
| sscanf(p, "%02X ", &info->glove_enabled); |
| pr_info("fts_feature_enable: Glove Enabled = %d\n", |
| info->glove_enabled); |
| break; |
| #endif |
| |
| #ifdef STYLUS_MODE |
| case FEAT_SEL_STYLUS: |
| sscanf(p, "%02X ", &info->stylus_enabled); |
| pr_info("fts_feature_enable: Stylus Enabled = %d\n", |
| info->stylus_enabled); |
| break; |
| #endif |
| |
| #ifdef COVER_MODE |
| case FEAT_SEL_COVER: |
| sscanf(p, "%02X ", &info->cover_enabled); |
| pr_info("fts_feature_enable: Cover Enabled = %d\n", |
| info->cover_enabled); |
| break; |
| #endif |
| |
| #ifdef CHARGER_MODE |
| case FEAT_SEL_CHARGER: |
| sscanf(p, "%02X ", &info->charger_enabled); |
| pr_info("fts_feature_enable: Charger Enabled = %d\n", |
| info->charger_enabled); |
| break; |
| #endif |
| |
| #ifdef GRIP_MODE |
| case FEAT_SEL_GRIP: |
| sscanf(p, "%02X ", &info->grip_enabled); |
| 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); |
| } |
| |
| 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 = FEAT_DISABLE; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = count; |
| |
| if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) { |
| pr_err("%s: bus is not accessible.", __func__); |
| goto exit; |
| } |
| |
| /* 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 { |
| res = sscanf(p, "%02X ", &temp); |
| if ((res != 1) || (temp > FEAT_ENABLE)) { |
| pr_err("%s: Missing or invalid grip mode(%u)\n", |
| __func__, temp); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| /* 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); |
| } |
| } |
| |
| exit: |
| fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); |
| return retval; |
| } |
| #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 = FEAT_DISABLE; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = count; |
| |
| if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) { |
| pr_err("%s: bus is not accessible.\n", __func__); |
| goto exit; |
| } |
| |
| /* 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); |
| retval = -EINVAL; |
| } else { |
| res = sscanf(p, "%02X ", &temp); |
| if ((res != 1) || (temp > FEAT_ENABLE)) { |
| pr_err("%s: Missing or invalid charger mode (%u)\n", |
| __func__, temp); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| /** 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); |
| } |
| } |
| |
| exit: |
| fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); |
| return retval; |
| } |
| #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 = FEAT_DISABLE; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = count; |
| |
| if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) { |
| pr_err("%s: bus is not accessible.\n", __func__); |
| goto exit; |
| } |
| |
| /* 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); |
| retval = -EINVAL; |
| } else { |
| res = sscanf(p, "%02X ", &temp); |
| if ((res != 1) || (temp > FEAT_ENABLE)) { |
| pr_err("%s: Missing or invalid glove mode(%u)\n", |
| __func__, temp); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| /* 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); |
| } |
| } |
| |
| exit: |
| fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); |
| return retval; |
| } |
| #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 = FEAT_DISABLE; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = count; |
| |
| if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) { |
| pr_err("%s: bus is not accessible.\n", __func__); |
| goto exit; |
| } |
| |
| /* 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 { |
| res = sscanf(p, "%02X ", &temp); |
| if ((res != 1) || (temp > FEAT_ENABLE)) { |
| pr_err("%s: Missing or invalid cover mode(%u)\n", |
| __func__, temp); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| 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); |
| } |
| } |
| |
| exit: |
| fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); |
| return retval; |
| } |
| #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 = FEAT_DISABLE; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = 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 { |
| res = sscanf(p, "%02X ", &temp); |
| if ((res != 1) || (temp > FEAT_ENABLE)) { |
| pr_err("%s: Missing or invalid stylus mode(%u)\n", |
| __func__, temp); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| info->stylus_enabled = temp; |
| } |
| |
| exit: |
| return retval; |
| } |
| #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); |
| goto exit; |
| } |
| |
| 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; |
| |
| exit: |
| 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, res; |
| unsigned int temp = 0; |
| ssize_t retval = count; |
| |
| 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++) { |
| res = sscanf(p, "%02X ", &temp); |
| if (res != 1) { |
| pr_err("%s: Invalid input\n", __func__); |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| p += 3; |
| mask[n] = (u8)temp; |
| pr_info("mask[%d] = %02X\n", n, mask[n]); |
| } |
| } |
| exit: |
| return retval; |
| } |
| |
| #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 = 0; |
| int res; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| ssize_t retval = count; |
| |
| if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) { |
| pr_err("%s: bus is not accessible.\n", __func__); |
| goto exit; |
| } |
| |
| 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; |
| retval = -EINVAL; |
| } else { |
| memset(mask, 0, GESTURE_MASK_SIZE + 2); |
| mask[0] = ((count + 1) / 3) - 1; |
| res = sscanf(p, "%02X ", &temp); |
| if (res != 1) { |
| pr_err("%s: Invalid input(%u)\n",__func__, temp); |
| mask[0] = 0; |
| retval = -EINVAL; |
| goto bad_param; |
| } |
| |
| p += 3; |
| mask[1] = (u8)temp; |
| for (n = 1; n < (count + 1) / 3; n++) { |
| res = sscanf(p, "%02X ", &temp); |
| if (res != 1) { |
| pr_err("%s: Invalid input\n", __func__); |
| mask[0] = 0; |
| retval = -EINVAL; |
| goto bad_param; |
| } |
| |
| p += 3; |
| fromIDtoMask((u8)temp, &mask[2], GESTURE_MASK_SIZE); |
| } |
| |
| for (n = 0; n < GESTURE_MASK_SIZE + 2; n++) |
| pr_info("mask[%d] = %02X\n", n, mask[n]); |
| } |
| |
| bad_param; |
| 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); |
| |
| goto exit; |
| } |
| |
| 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); |
| |
| exit: |
| fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); |
| return retval; |
| } |
| |
| |
| #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 |
| |
| |
| |
| /***************************************** 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) |
| { |
| int n; |
| char *p = (char *)buf; |
| struct fts_ts_info *info = dev_get_drvdata(dev); |
| |
| memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32)); |
| |
| pr_info("%s:\n", __func__); |
| |
| 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; |
| } |
| |
| for (n = 0; n < (count + 1) / 3; n++) { |
| sscanf(p, "%02X ", &typeOfComand[n]); |
| p += 3; |
| pr_info("typeOfComand[%d] = %02X\n", n, typeOfComand[n]); |
| } |
| |
| numberParameters = n; |
| pr_info("Number of Parameters = %d\n", numberParameters); |
| |
| mutex_unlock(&info->diag_cmd_lock); |
| |
| return count; |
| } |
| |
| 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); |
| |
| MutualSenseData compData; |
| SelfSenseData comData; |
| MutualSenseFrame frameMS; |
| SelfSenseFrame frameSS; |
| |
| 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_disableInterrupt(); |
| if (res < 0) { |
| pr_err("fts_disableInterrupt: ERROR %08X\n", res); |
| res = (res | ERROR_DISABLE_INTER); |
| goto END; |
| } |
| |
| switch (typeOfComand[0]) { |
| /*ITO TEST*/ |
| case 0x01: |
| res = production_test_ito(LIMITS_FILE, &tests); |
| 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: |
| 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_main(LIMITS_FILE, 1, init_type, |
| &tests); |
| break; |
| |
| /*read mutual raw*/ |
| case 0x13: |
| if (numberParameters > 1) { |
| pr_info("Get 1 MS Frame\n"); |
| setScanMode(SCAN_MODE_LOCKED, typeOfComand[1]); |
| mdelay(WAIT_FOR_FRESH_FRAMES); |
| setScanMode(SCAN_MODE_ACTIVE, 0x00); |
| mdelay(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(); |
| res = getMSFrame3(MS_RAW, &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); |
| #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); |
| } |
| } else { |
| pr_err("Wrong number of parameters!\n"); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| break; |
| /*read self raw*/ |
| case 0x15: |
| if (numberParameters > 1) { |
| pr_info("Get 1 SS Frame\n"); |
| setScanMode(SCAN_MODE_LOCKED, typeOfComand[1]); |
| 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 = getSSFrame3(SS_RAW, &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); |
| #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); |
| } |
| } else { |
| pr_err("Wrong number of parameters!\n"); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| |
| 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 + 6) * |
| 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"); |
| 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)(typeOfComand[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 (typeOfComand[0] != 0xF0) |
| doClean |= fts_enableInterrupt(); |
| 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) { |
| /*all the other cases are already fine printing only the res.*/ |
| switch (typeOfComand[0]) { |
| case 0x13: |
| case 0x17: |
| #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, "%7d", |
| 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, |
| "%7d", |
| 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, "%7d", |
| 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); |
| |
| /* 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 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 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 |
| |
| /* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ |
| static struct attribute *fts_attr_group[] = { |
| &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 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 |
| NULL, |
| }; |
| |
| /** @}*/ |
| /** @}*/ |
| |
| |
| /** |
| * @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 |
| */ |
| pr_err("%s: Pressure is %i, but pointer is not leaving\n", |
| __func__, z); |
| 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--; |
| |
| if (y == info->board->y_axis_max) |
| y--; |
| |
| 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: |
| case TOUCH_TYPE_PALM: |
| pr_debug("%s : It is a touch type %d!\n", __func__, touchType); |
| tool = MT_TOOL_FINGER; |
| touch_condition = 1; |
| __set_bit(touchId, &info->touch_id); |
| 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; |
| |
| case TOUCH_TYPE_INVALID: |
| 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); |
| input_report_abs(info->input_dev, ABS_MT_PRESSURE, z); |
| |
| #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_HOVER: |
| tool = MT_TOOL_FINGER; |
| __clear_bit(touchId, &info->touch_id); |
| break; |
| |
| case TOUCH_TYPE_INVALID: |
| 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(); |
| if (error < OK) |
| pr_err("%s Cannot restore the device ERROR %08X\n", |
| __func__, error); |
| } |
| break; |
| 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(); |
| 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) |
| { |
| 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_FORCE_CAL: |
| switch (event[2]) { |
| case 0x00: |
| pr_info("%s: Continuous 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 0x01: |
| pr_info("%s: Mutual negative detect 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: Mutual calib deviation 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: SS negative detect Force cal = %02X %02X %02X %02X %02X %02X\n", |
| __func__, event[2], event[3], event[4], |
| event[5], event[6], event[7]); |
| break; |
| |
| case 0x12: |
| pr_info("%s: SS negative detect Force cal in Low Power mode = %02X %02X %02X %02X %02X %02X\n", |
| __func__, event[2], event[3], event[4], |
| event[5], event[6], event[7]); |
| break; |
| |
| case 0x13: |
| pr_info("%s: SS negative detect Force cal in Idle mode = %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: SS invalid Mutual Strength soft 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: SS invalid Self Strength soft 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: SS invalid Self Island soft 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: MS invalid Mutual Strength soft 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: MS invalid Self Strength soft 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: 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: |
| switch (event[2]) { |
| case 0x01: |
| pr_info("%s: Frame drop noisy 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: Frame drop bad R = %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: Frame drop invalid processing state = %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: 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: |
| if (event[2] == 1) |
| pr_info("%s: Enter Water mode = %02X %02X %02X %02X %02X %02X\n", |
| __func__, event[2], event[3], event[4], |
| event[5], event[6], event[7]); |
| else |
| pr_info("%s: Exit Water mode = %02X %02X %02X %02X %02X %02X\n", |
| __func__, event[2], event[3], event[4], |
| event[5], event[6], event[7]); |
| break; |
| |
| default: |
| pr_err("%s: Received unhandled 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; |
| } |
| |
| static void heatmap_enable(void) |
| { |
| unsigned char command[] = {0xA4, 0x06, LOCAL_HEATMAP_MODE}; |
| fts_write(command, 3); |
| } |
| |
| static bool read_heatmap_raw(struct v4l2_heatmap *v4l2, strength_t *data) |
| { |
| unsigned char heatmap_read_command[] = {0xA6, 0x00, 0x00}; |
| |
| 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}; |
| |
| result = fts_writeRead(heatmap_read_command, 3, |
| (uint8_t *) &report, sizeof(report)); |
| 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); |
| 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. |
| */ |
| pr_warn("Heatmap frame has stale counter value %i", |
| counter); |
| } |
| counter <
|