blob: b523ad83a3460a08268869a64815923530df199b [file] [log] [blame]
/**
* 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);
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 + 1]; /* extra byte to hold '\0'*/
struct fts_ts_info *info = dev_get_drvdata(dev);
/* default(if not specified by user) set force = 0 and keep_cx to 1 */
mode[0] = 0;
mode[1] = 1;
/* reading out firmware upgrade parameters */
if (sscanf(buf, "%100s %d %d", path, &mode[0], &mode[1]) >= 1) {
pr_info("%s: file = %s, force = %d, keep_cx = %d\n", __func__,
path, mode[0], mode[1]);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true);
if (info->sensor_sleep)
ret = ERROR_BUS_WR;
else
ret = flashProcedure(path, mode[0], mode[1]);
info->fwupdate_stat = ret;
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
if (ret == ERROR_BUS_WR)
pr_err("%s: bus is not accessible. ERROR %08X\n",
__func__, ret);
else if (ret < OK)
pr_err("%s Unable to upgrade firmware! ERROR %08X\n",
__func__, ret);
} else
pr_err("%s: Wrong number of parameters! ERROR %08X\n",
__func__, ERROR_OP_NOT_ALLOW);
return count;
}
static ssize_t fts_fwupdate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
/* fwupdate_stat: ERROR code Returned by flashProcedure. */
return scnprintf(buf, PAGE_SIZE, "{ %08X }\n", info->fwupdate_stat);
}
/***************************************** UTILITIES
* (current fw_ver/conf_id, active mode, file fw_ver/conf_id)
***************************************************/
/**
* File node to show on terminal external release version in Little Endian \n
* (first the less significant byte) \n
* cat appid show the external release version of the FW running in the IC
*/
static ssize_t fts_appid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
int written = 0;
char temp[35];
written += scnprintf(buf + written, PAGE_SIZE - written,
"REL: %s\n",
printHex("",
systemInfo.u8_releaseInfo,
EXTERNAL_RELEASE_INFO_SIZE,
temp,
sizeof(temp)));
written += scnprintf(buf + written, PAGE_SIZE - written,
"FW: %04X\nCFG: %04X\nAFE: %02X\nProject: %04X\n",
systemInfo.u16_fwVer, systemInfo.u16_cfgVer,
systemInfo.u8_cfgAfeVer,
systemInfo.u16_cfgProjectId);
written += scnprintf(buf + written, PAGE_SIZE - written,
"FW file: %s\n", info->board->fw_name);
written += scnprintf(buf + written, PAGE_SIZE - written,
"Extended display info: ");
if (!info->extinfo.is_read)
written += scnprintf(buf + written, PAGE_SIZE - written,
"[pending]");
else if (info->extinfo.size == 0)
written += scnprintf(buf + written, PAGE_SIZE - written,
"[none]");
else if (info->extinfo.size * 2 < PAGE_SIZE - written) {
bin2hex(buf + written, info->extinfo.data, info->extinfo.size);
written += info->extinfo.size * 2;
}
written += scnprintf(buf + written, PAGE_SIZE - written,
"\nMPFlag: %02X\n",
systemInfo.u8_mpFlag);
return written;
}
/**
* File node to show on terminal the mode that is active on the IC \n
* cat mode_active to show the bitmask which indicate
* the modes/features which are running on the IC in a specific instant of time
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1 = 1 byte in HEX format which represent the actual running scan mode
* (@link scan_opt Scan Mode Options @endlink) \n
* X2 = 1 byte in HEX format which represent the bitmask on which is running
* the actual scan mode \n
* X3X4 = 2 bytes in HEX format which represent a bitmask of the features that
* are enabled at this moment (@link feat_opt Feature Selection Options
* @endlink) \n
* } = end byte
* @see fts_mode_handler()
*/
static ssize_t fts_mode_active_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("Current mode active = %08X\n", info->mode);
return scnprintf(buf, PAGE_SIZE, "{ %08X }\n", info->mode);
}
/**
* File node to show the fw_ver and config_id of the FW file
* cat fw_file_test show on the kernel log external release
* of the FW stored in the fw file/header file
*/
static ssize_t fts_fw_test_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
Firmware fw;
int ret;
char temp[100] = { 0 };
fw.data = NULL;
ret = readFwFile(info->board->fw_name, &fw, 0);
if (ret < OK)
pr_err("Error during reading FW file! ERROR %08X\n", ret);
else
pr_info("%s, size = %d bytes\n",
printHex("EXT Release = ",
systemInfo.u8_releaseInfo,
EXTERNAL_RELEASE_INFO_SIZE,
temp,
sizeof(temp)),
fw.data_size);
kfree(fw.data);
return 0;
}
static ssize_t fts_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
u8 *dump = NULL;
int dumpSize = ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE;
u8 reg;
int written = 0;
int res;
int i;
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
pr_err("%s: bus is not accessible.\n", __func__);
written += scnprintf(buf, PAGE_SIZE,
"Bus is not accessible.\n");
goto exit;
}
written += scnprintf(buf + written, PAGE_SIZE - written,
"Mode: 0x%08X\n", info->mode);
res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, ADDR_ICR,
&reg, 1, DUMMY_HW_REG);
if (res < 0)
pr_err("%s: failed to read ICR.\n", __func__);
else
written += scnprintf(buf + written, PAGE_SIZE - written,
"ICR: 0x%02X\n", reg);
dump = kzalloc(dumpSize, GFP_KERNEL);
if (!dump) {
written += strlcat(buf + written, "Buffer allocation failed!\n",
PAGE_SIZE - written);
goto exit;
}
res = dumpErrorInfo(dump, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE);
if (res >= 0) {
written += strlcat(buf + written, "Error dump:",
PAGE_SIZE - written);
for (i = 0; i < dumpSize; i++) {
if (i % 8 == 0)
written += scnprintf(buf + written,
PAGE_SIZE - written,
"\n%02X: ", i);
written += scnprintf(buf + written,
PAGE_SIZE - written,
"%02X ", dump[i]);
}
written += strlcat(buf + written, "\n", PAGE_SIZE - written);
}
exit:
kfree(dump);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return written;
}
#if 0
/**
* File node to obtain and show strength frame
* cat strength_frame to obtain strength data \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 no
*error) \n
* **** if error code is all 0s **** \n
* FF = 1 byte in HEX format number of rows \n
* SS = 1 byte in HEX format number of columns \n
* N1, ... = the decimal value of each node separated by a coma \n
* ********************************* \n
* } = end byte
*/
static ssize_t fts_strength_frame_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
MutualSenseFrame frame;
int res, count, j, size = (6 * 2) + 1, index = 0;
char *all_strbuff = NULL;
/* char buff[CMD_STR_LEN] = {0}; */
/* struct i2c_client *client = to_i2c_client(dev); */
struct fts_ts_info *info = dev_get_drvdata(dev);
frame.node_data = NULL;
res = fts_enableInterrupt(false);
if (res < OK)
goto END;
res = senseOn();
if (res < OK) {
pr_err("%s: could not start scanning! ERROR %08X\n",
__func__, res);
goto END;
}
mdelay(WAIT_FOR_FRESH_FRAMES);
res = senseOff();
if (res < OK) {
pr_err("%s: could not finish scanning! ERROR %08X\n",
__func__, res);
goto END;
}
mdelay(WAIT_AFTER_SENSEOFF);
flushFIFO();
res = getMSFrame3(MS_STRENGTH, &frame);
if (res < OK) {
pr_err("%s: could not get the frame! ERROR %08X\n",
__func__, res);
goto END;
} else {
size += (res * 6);
pr_info("The frame size is %d words\n", res);
res = OK;
print_frame_short("MS Strength frame =", array1dTo2d_short(
frame.node_data, frame.node_data_size,
frame.header.sense_node),
frame.header.force_node,
frame.header.sense_node);
}
END:
flushFIFO();
release_all_touches(info);
fts_mode_handler(info, 1);
all_strbuff = (char *)kzalloc(size * sizeof(char), GFP_KERNEL);
if (all_strbuff != NULL) {
snprintf(&all_strbuff[index], 11, "{ %08X", res);
index += 10;
if (res >= OK) {
snprintf(&all_strbuff[index], 3, "%02X",
(u8)frame.header.force_node);
index += 2;
snprintf(&all_strbuff[index], 3, "%02X",
(u8)frame.header.sense_node);
index += 2;
for (j = 0; j < frame.node_data_size; j++) {
snprintf(&all_strbuff[index], 10, "%d,%n",
frame.node_data[j], &count);
index += count;
}
kfree(frame.node_data);
}
snprintf(&all_strbuff[index], 3, " }");
index += 2;
count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff);
kfree(all_strbuff);
} else
pr_err("%s: Unable to allocate all_strbuff! ERROR %08X\n",
__func__, ERROR_ALLOC);
fts_enableInterrupt(true);
return count;
}
#endif
/***************************************** FEATURES
***************************************************/
/* TODO: edit this function according to the features policy to allow during
* the screen on/off, following is shown an example but check always with ST
* for more details */
/**
* Check if there is any conflict in enable/disable a particular feature
* considering the features already enabled and running
* @param info pointer to fts_ts_info which contains info about the device
* and its hw setup
* @param feature code of the feature that want to be tested
* @return OK if is possible to enable/disable feature, ERROR_OP_NOT_ALLOW
* in case of any other conflict
*/
int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature)
{
int res = OK;
/* Example based on the status of the screen and on the feature
* that is trying to enable */
/*res=ERROR_OP_NOT_ALLOW;
* if(info->resume_bit ==0){
* switch(feature){
#ifdef GESTURE_MODE
* case FEAT_SEL_GESTURE:
* res = OK;
* break;
#endif
* default:
* pr_err("%s: Feature not allowed in this
* operating mode! ERROR %08X\n", __func__, res);
* break;
*
* }
* }else{
* switch(feature){
#ifdef GESTURE_MODE
* case FEAT_SEL_GESTURE:
#endif
* case FEAT__SEL_GLOVE: // glove mode can only activate
*during sense on
* res = OK;
* break;
*
* default:
* pr_err("%s: Feature not allowed in this
* operating mode! ERROR %08X\n", __func__, res);
* break;
*
* }
* }*/
/* Example based only on the feature that is going to be activated */
switch (feature) {
case FEAT_SEL_GESTURE:
if (info->cover_enabled == 1) {
res = ERROR_OP_NOT_ALLOW;
pr_err("%s: Feature not allowed when in Cover mode! ERROR %08X\n",
__func__, res);
/* for example here can be placed a code for disabling
* the cover mode when gesture is activated */
}
break;
case FEAT_SEL_GLOVE:
if (info->gesture_enabled == 1) {
res = ERROR_OP_NOT_ALLOW;
pr_err("%s: Feature not allowed when Gestures enabled! ERROR %08X\n",
__func__, res);
/* for example here can be placed a code for disabling
* the gesture mode when cover is activated
* (that means that cover mode has
* an higher priority on gesture mode) */
}
break;
default:
pr_info("%s: Feature Allowed!\n", __func__);
}
return res;
}
#ifdef USE_ONE_FILE_NODE
/**
* File node to enable some feature
* echo XX 00/01 > feature_enable to enable/disable XX
* (possible values @link feat_opt Feature Selection Options @endlink) feature
* cat feature_enable to show the result of enabling/disabling process
* echo 01/00 > feature_enable; cat feature_enable to perform
* both actions stated before in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 =
* no error) \n
* } = end byte
*/
static ssize_t fts_feature_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
char *p = (char *)buf;
unsigned int temp, temp2;
int res = OK;
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
if ((count - 2 + 1) / 3 != 1)
pr_err("fts_feature_enable: Number of parameter wrong! %d > %d\n",
(count - 2 + 1) / 3, 1);
else {
if (sscanf(p, "%02X %02X ", &temp, &temp2) == 2) {
p += 3;
res = check_feature_feasibility(info, temp);
if (res >= OK) {
switch (temp) {
#ifdef GESTURE_MODE
case FEAT_SEL_GESTURE:
info->gesture_enabled = temp2;
pr_info("fts_feature_enable: Gesture Enabled = %d\n",
info->gesture_enabled);
break;
#endif
#ifdef GLOVE_MODE
case FEAT_SEL_GLOVE:
info->glove_enabled = temp2;
pr_info("fts_feature_enable: Glove Enabled = %d\n",
info->glove_enabled);
break;
#endif
#ifdef STYLUS_MODE
case FEAT_SEL_STYLUS:
info->stylus_enabled = temp2;
pr_info("fts_feature_enable: Stylus Enabled = %d\n",
info->stylus_enabled);
break;
#endif
#ifdef COVER_MODE
case FEAT_SEL_COVER:
info->cover_enabled = temp2;
pr_info("fts_feature_enable: Cover Enabled = %d\n",
info->cover_enabled);
break;
#endif
#ifdef CHARGER_MODE
case FEAT_SEL_CHARGER:
info->charger_enabled = temp2;
pr_info("fts_feature_enable: Charger Enabled = %d\n",
info->charger_enabled);
break;
#endif
#ifdef GRIP_MODE
case FEAT_SEL_GRIP:
info->grip_enabled = temp2;
pr_info("fts_feature_enable: Grip Enabled = %d\n",
info->grip_enabled);
break;
#endif
default:
pr_err("fts_feature_enable: Feature %08X not valid! ERROR %08X\n",
temp, ERROR_OP_NOT_ALLOW);
res = ERROR_OP_NOT_ALLOW;
}
feature_feasibility = res;
}
if (feature_feasibility >= OK)
feature_feasibility = fts_mode_handler(info, 1);
else
pr_err("%s: Call echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n",
__func__, res);
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
static ssize_t fts_feature_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
if (feature_feasibility < OK)
pr_err("%s: Call before echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n",
__func__, feature_feasibility);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
feature_feasibility);
feature_feasibility = ERROR_OP_NOT_ALLOW;
return count;
}
#else
#ifdef GRIP_MODE
/**
* File node to set the grip mode
* echo 01/00 > grip_mode to enable/disable glove mode \n
* cat grip_mode to show the status of the grip_enabled switch \n
* echo 01/00 > grip_mode; cat grip_mode to enable/disable grip
*mode
* and see the switch status in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent the value
* info->grip_enabled (1 = enabled; 0= disabled) \n
* } = end byte
*/
static ssize_t fts_grip_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("%s: grip_enabled = %d\n", __func__,
info->grip_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->grip_enabled);
return count;
}
static ssize_t fts_grip_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
char *p = (char *)buf;
unsigned int temp;
int res;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
/* in case of a different elaboration of the input, just modify
* this initial part of the code according to customer needs */
if ((count + 1) / 3 != 1)
pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
__func__, (count + 1) / 3);
else {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
/* standard code that should be always used when a feature is enabled! */
/* first step : check if the wanted feature can be enabled */
/* second step: call fts_mode_handler to actually enable it */
/* NOTE: Disabling a feature is always allowed by default */
res = check_feature_feasibility(info, FEAT_SEL_GRIP);
if (res >= OK || temp == FEAT_DISABLE) {
info->grip_enabled = temp;
res = fts_mode_handler(info, 1);
if (res < OK)
pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
__func__, res);
}
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
#endif
#ifdef CHARGER_MODE
/**
* File node to set the glove mode
* echo XX/00 > charger_mode to value >0 to enable
* (possible values: @link charger_opt Charger Options @endlink),
* 00 to disable charger mode \n
* cat charger_mode to show the status of the charger_enabled switch \n
* echo 01/00 > charger_mode; cat charger_mode to enable/disable
* charger mode and see the switch status in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent the value
* info->charger_enabled (>0 = enabled; 0= disabled) \n
* } = end byte
*/
static ssize_t fts_charger_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("%s: charger_enabled = %d\n", __func__,
info->charger_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->charger_enabled);
return count;
}
static ssize_t fts_charger_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
char *p = (char *)buf;
unsigned int temp;
int res;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.\n", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
/* in case of a different elaboration of the input, just modify this
* initial part of the code according to customer needs */
if ((count + 1) / 3 != 1)
pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
__func__, (count + 1) / 3);
else {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
/** standard code that should be always used when a feature is enabled!
* first step : check if the wanted feature can be enabled
* second step: call fts_mode_handler to actually enable it
* NOTE: Disabling a feature is always allowed by default
*/
res = check_feature_feasibility(info, FEAT_SEL_CHARGER);
if (res >= OK || temp == FEAT_DISABLE) {
info->charger_enabled = temp;
res = fts_mode_handler(info, 1);
if (res < OK)
pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
__func__, res);
}
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
#endif
#ifdef GLOVE_MODE
/**
* File node to set the glove mode
* echo 01/00 > glove_mode to enable/disable glove mode \n
* cat glove_mode to show the status of the glove_enabled switch \n
* echo 01/00 > glove_mode; cat glove_mode to enable/disable glove mode and
* see the switch status in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent the of value
* info->glove_enabled (1 = enabled; 0= disabled) \n
* } = end byte
*/
static ssize_t fts_glove_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("%s: glove_enabled = %d\n", __func__, info->glove_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->glove_enabled);
return count;
}
static ssize_t fts_glove_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
char *p = (char *)buf;
unsigned int temp;
int res;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.\n", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
/* in case of a different elaboration of the input, just modify this
* initial part of the code according to customer needs */
if ((count + 1) / 3 != 1)
pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
__func__, (count + 1) / 3);
else {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
/* standard code that should be always used when a feature is enabled! */
/* first step : check if the wanted feature can be enabled */
/* second step: call fts_mode_handler to actually enable it */
/* NOTE: Disabling a feature is always allowed by default */
res = check_feature_feasibility(info, FEAT_SEL_GLOVE);
if (res >= OK || temp == FEAT_DISABLE) {
info->glove_enabled = temp;
res = fts_mode_handler(info, 1);
if (res < OK)
pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
__func__, res);
}
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
#endif
#ifdef COVER_MODE
/* echo 01/00 > cover_mode to enable/disable cover mode */
/* cat cover_mode to show the status of the cover_enabled switch
* (example output in the terminal = "AA00000001BB" if the switch is enabled) */
/* echo 01/00 > cover_mode; cat cover_mode to enable/disable cover mode and
* see the switch status in just one call */
/* NOTE: the cover can be handled also using a notifier, in this case the body
* of these functions should be copied in the notifier callback */
/**
* File node to set the cover mode
* echo 01/00 > cover_mode to enable/disable cover mode \n
* cat cover_mode to show the status of the cover_enabled switch \n
* echo 01/00 > cover_mode; cat cover_mode to enable/disable cover mode
* and see the switch status in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which is the value of info->cover_enabled
* (1 = enabled; 0= disabled)\n
* } = end byte \n
* NOTE: \n
* the cover can be handled also using a notifier, in this case the body of
* these functions should be copied in the notifier callback
*/
static ssize_t fts_cover_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("%s: cover_enabled = %d\n", __func__, info->cover_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->cover_enabled);
return count;
}
static ssize_t fts_cover_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
char *p = (char *)buf;
unsigned int temp;
int res;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.\n", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
/* in case of a different elaboration of the input, just modify this
* initial part of the code according to customer needs */
if ((count + 1) / 3 != 1)
pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
__func__, (count + 1) / 3);
else {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
/* standard code that should be always used when a feature is enabled! */
/* first step : check if the wanted feature can be enabled */
/* second step: call fts_mode_handler to actually enable it */
/* NOTE: Disabling a feature is always allowed by default */
res = check_feature_feasibility(info, FEAT_SEL_COVER);
if (res >= OK || temp == FEAT_DISABLE) {
info->cover_enabled = temp;
res = fts_mode_handler(info, 1);
if (res < OK)
pr_err("%s: Error during fts_mode_handler! ERROR %08X\n",
__func__, res);
}
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
#endif
#ifdef STYLUS_MODE
/**
* File node to enable the stylus report
* echo 01/00 > stylus_mode to enable/disable stylus mode \n
* cat stylus_mode to show the status of the stylus_enabled switch \n
* echo 01/00 > stylus_mode; cat stylus_mode to enable/disable stylus mode
* and see the switch status in just one call \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which is the value of info->stylus_enabled
* (1 = enabled; 0= disabled)\n
* } = end byte
*/
static ssize_t fts_stylus_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("%s: stylus_enabled = %d\n", __func__, info->stylus_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->stylus_enabled);
return count;
}
static ssize_t fts_stylus_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
char *p = (char *)buf;
unsigned int temp;
struct fts_ts_info *info = dev_get_drvdata(dev);
/* in case of a different elaboration of the input, just modify this
* initial part of the code according to customer needs */
if ((count + 1) / 3 != 1)
pr_err("%s: Number of bytes of parameter wrong! %zu != 1 byte\n",
__func__, (count + 1) / 3);
else {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
info->stylus_enabled = temp;
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
return count;
}
#endif
#endif
/***************************************** GESTURES
***************************************************/
#ifdef GESTURE_MODE
#ifdef USE_GESTURE_MASK /* if this define is used, a gesture bit mask
* is used as method to select the gestures to
* enable/disable */
/**
* File node used by the host to set the gesture mask to enable or disable
* echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
* EE = 00(disable) or 01(enable) \n
* X1 ~~ = gesture mask (example 06 00 ~~ 00 this gesture mask represents
* the gestures with ID = 1 and 2) can be specified
* from 1 to GESTURE_MASK_SIZE bytes, \n
* if less than GESTURE_MASK_SIZE bytes are passed as arguments,
* the omit bytes of the mask maintain the previous settings \n
* if one or more gestures is enabled the driver will automatically
* enable the gesture mode, If all the gestures are disabled the driver
* automatically will disable the gesture mode \n
* cat gesture_mask set inside the specified mask and return an error code
* for the operation \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent an error code for enabling
* the mask (00000000 = no error)\n
* } = end byte \n\n
* if USE_GESTURE_MASK is not define the usage of the function become: \n\n
* echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
* EE = 00(disable) or 01(enable) \n
* X1 ~~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2
* and 5)
* there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture
* IDs @endlink) \n
* if one or more gestures is enabled the driver will automatically enable
* the gesture mode. If all the gestures are disabled the driver automatically
* will disable the gesture mode. \n
* cat gesture_mask to show the status of the gesture enabled switch \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled
* (1 = enabled; 0= disabled)\n
* } = end byte
*/
static ssize_t fts_gesture_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0, res, temp;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.\n", __func__);
scnprintf(buf, PAGE_SIZE, "{ %08X }\n", res);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
if (mask[0] == 0) {
res = ERROR_OP_NOT_ALLOW;
pr_err("%s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n",
__func__, res);
} else {
if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE)
res = updateGestureMask(&mask[2], mask[0], mask[1]);
else
res = ERROR_OP_NOT_ALLOW;
if (res < OK)
pr_err("fts_gesture_mask_store: ERROR %08X\n", res);
}
res |= check_feature_feasibility(info, FEAT_SEL_GESTURE);
temp = isAnyGestureActive();
if (res >= OK || temp == FEAT_DISABLE)
info->gesture_enabled = temp;
pr_info("fts_gesture_mask_store: Gesture Enabled = %d\n",
info->gesture_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n", res);
mask[0] = 0;
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
static ssize_t fts_gesture_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
char *p = (char *)buf;
int n;
unsigned int temp;
if ((count + 1) / 3 > GESTURE_MASK_SIZE + 1) {
pr_err("fts_gesture_mask_store: Number of bytes of parameter wrong! %zu > (enable/disable + %d )\n",
(count + 1) / 3, GESTURE_MASK_SIZE);
mask[0] = 0;
} else {
mask[0] = ((count + 1) / 3) - 1;
for (n = 1; n <= (count + 1) / 3; n++) {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
mask[n] = (u8)temp;
pr_info("mask[%d] = %02X\n", n, mask[n]);
} else
pr_err("%s: Error when reading with sscanf!\n",
__func__);
}
}
return count;
}
#else /* if this define is not used, to select the gestures to enable/disable
* are used the IDs of the gestures */
/* echo EE X1 X2 ... > gesture_mask set the gesture to disable/enable;
* EE = 00(disable) or 01(enable); X1 ... = gesture IDs
* (example 01 02 05... represent the gestures with ID = 1, 2 and 5)
* there is no limit of the parameters that can be passed,
* of course the gesture IDs should be valid (all the valid IDs are listed in
* ftsGesture.h) */
/* cat gesture_mask enable/disable the given gestures, if one or more
* gestures is enabled the driver will automatically enable the gesture mode.
* If all the gestures are disabled the driver automatically will disable the
* gesture mode.
* At the end an error code will be printed
* (example output in the terminal = "AA00000000BB" if there are no errors) */
/* echo EE X1 X2 ... > gesture_mask; cat gesture_mask perform in one command
* both actions stated before */
/**
* File node used by the host to set the gesture mask to enable or disable
* echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable;
* EE = 00(disable) or 01(enable) \n
* X1 ~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2
* and 5)
* there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture
* IDs @endlink) \n
* if one or more gestures is enabled the driver will automatically enable
* the gesture mode, If all the gestures are disabled the driver automatically
* will disable the gesture mode \n
* cat gesture_mask to show the status of the gesture enabled switch \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled
* (1 = enabled; 0= disabled)\n
* } = end byte
*/
static ssize_t fts_gesture_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
struct fts_ts_info *info = dev_get_drvdata(dev);
pr_info("fts_gesture_mask_show: gesture_enabled = %d\n",
info->gesture_enabled);
count += scnprintf(buf + count,
PAGE_SIZE - count, "{ %08X }\n",
info->gesture_enabled);
return count;
}
static ssize_t fts_gesture_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
char *p = (char *)buf;
int n;
unsigned int temp;
int res;
struct fts_ts_info *info = dev_get_drvdata(dev);
if (fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, true) < 0) {
res = ERROR_BUS_WR;
pr_err("%s: bus is not accessible.\n", __func__);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) {
pr_err("fts_gesture_mask_store: Number of bytes of parameter wrong! %d < or > (enable/disable + at least one gestureID or max %d bytes)\n",
(count + 1) / 3, GESTURE_MASK_SIZE);
mask[0] = 0;
} else {
memset(mask, 0, GESTURE_MASK_SIZE + 2);
mask[0] = ((count + 1) / 3) - 1;
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
mask[1] = (u8)temp;
for (n = 1; n < (count + 1) / 3; n++) {
if (sscanf(p, "%02X ", &temp) == 1) {
p += 3;
fromIDtoMask((u8)temp, &mask[2],
GESTURE_MASK_SIZE);
} else {
pr_err("%s: Error when reading with sscanf!\n",
__func__);
mask[0] = 0;
goto END;
}
}
for (n = 0; n < GESTURE_MASK_SIZE + 2; n++)
pr_info("mask[%d] = %02X\n", n, mask[n]);
} else {
pr_err("%s: Error when reading with sscanf!\n",
__func__);
mask[0] = 0;
}
}
END:
if (mask[0] == 0) {
res = ERROR_OP_NOT_ALLOW;
pr_err("%s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n",
__func__, res);
} else {
if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE)
res = updateGestureMask(&mask[2], mask[0], mask[1]);
else
res = ERROR_OP_NOT_ALLOW;
if (res < OK)
pr_err("fts_gesture_mask_store: ERROR %08X\n", res);
}
res = check_feature_feasibility(info, FEAT_SEL_GESTURE);
temp = isAnyGestureActive();
if (res >= OK || temp == FEAT_DISABLE)
info->gesture_enabled = temp;
res = fts_mode_handler(info, 0);
fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false);
return count;
}
#endif
/**
* File node to read the coordinates of the last gesture drawn by the user \n
* cat gesture_coordinates to obtain the gesture coordinates \n
* the string returned in the shell follow this up as follow: \n
* { = start byte \n
* X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 =
*OK) \n
* \n if error code = 00000000 \n
* CC = 1 byte in HEX format number of coords (pair of x,y) returned \n
* XXiYYi ... = XXi 2 bytes in HEX format for x[i] and
* YYi 2 bytes in HEX format for y[i] (big endian) \n
* \n
* } = end byte
*/
static ssize_t fts_gesture_coordinates_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int size = PAGE_SIZE;
int count = 0, res, i = 0;
pr_info("%s: Getting gestures coordinates...\n", __func__);
if (gesture_coords_reported < OK) {
pr_err("%s: invalid coordinates! ERROR %08X\n",
__func__, gesture_coords_reported);
res = gesture_coords_reported;
} else {
size += gesture_coords_reported * 2 * 4 + 2;
/* coords are pairs of x,y (*2) where each coord is
* short(2bytes=4char)(*4) + 1 byte(2char) num of coords (+2)
**/
res = OK; /* set error code to OK */
}
count += scnprintf(buf + count,
size - count, "{ %08X", res);
if (res >= OK) {
count += scnprintf(buf + count,
size - count, "%02X",
gesture_coords_reported);
for (i = 0; i < gesture_coords_reported; i++) {
count += scnprintf(buf + count,
size - count,
"%04X",
gesture_coordinates_x[i]);
count += scnprintf(buf + count,
size - count,
"%04X",
gesture_coordinates_y[i]);
}
}
count += scnprintf(buf + count, size - count, " }\n");
pr_info("%s: Getting gestures coordinates FINISHED!\n", __func__);
return count;
}
#endif
/* Touch simulation hr timer expiry callback */
static enum hrtimer_restart touchsim_timer_cb(struct hrtimer *timer)
{
struct fts_touchsim *touchsim = container_of(timer,
struct fts_touchsim,
hr_timer);
enum hrtimer_restart retval = HRTIMER_NORESTART;
if (touchsim->is_running) {
hrtimer_forward_now(timer,
ns_to_ktime(TOUCHSIM_TIMER_INTERVAL_NS));
retval = HRTIMER_RESTART;
}
/* schedule the task to report touch coordinates to kernel
* input subsystem
*/
queue_work(touchsim->wq, &touchsim->work);
return retval;
}
/* Compute the next touch coordinate(x,y) */
static void touchsim_refresh_coordinates(struct fts_touchsim *touchsim)
{
struct fts_ts_info *info = container_of(touchsim,
struct fts_ts_info,
touchsim);
const int x_start = info->board->x_axis_max / 10;
const int x_max = (info->board->x_axis_max * 9) / 10;
const int y_start = info->board->y_axis_max / 4;
const int y_max = info->board->y_axis_max / 2;
touchsim->x += touchsim->x_step;
touchsim->y += touchsim->y_step;
if (touchsim->x < x_start || touchsim->x > x_max)
touchsim->x_step *= -1;
if (touchsim->y < y_start || touchsim->y > y_max)
touchsim->y_step *= -1;
}
/* Report touch contact */
static void touchsim_report_contact_event(struct input_dev *dev, int slot_id,
int x, int y, int z)
{
/* report the cordinates to the input subsystem */
input_mt_slot(dev, slot_id);
input_report_key(dev, BTN_TOUCH, true);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
input_report_abs(dev, ABS_MT_POSITION_X, x);
input_report_abs(dev, ABS_MT_POSITION_Y, y);
input_report_abs(dev, ABS_MT_PRESSURE, z);
}
/* Work callback to report the touch co-ordinates to input subsystem */
static void touchsim_work(struct work_struct *work)
{
struct fts_touchsim *touchsim = container_of(work,
struct fts_touchsim,
work);
struct fts_ts_info *info = container_of(touchsim,
struct fts_ts_info,
touchsim);
u64 timestamp_ns = ktime_get_ns();
/* 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_event(info->input_dev, EV_MSC, MSC_TIMESTAMP,
timestamp_ns / 1000);
input_sync(info->input_dev);
heatmap_read(&info->v4l2, timestamp_ns);
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);
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();
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);
}
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);
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)
res = getSSFrame3(SS_DETECT_RAW, &frameSS);
else
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);
}
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");
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
*/
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);
}
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);
static DEVICE_ATTR(heatmap_mode, 0664, fts_heatmap_mode_show,
fts_heatmap_mode_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
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,
&dev_attr_heatmap_mode.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
&dev_attr_autotune.attr,
&dev_attr_touchsim.attr,
&dev_attr_default_mf.attr,
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<