blob: 684f2e7440c5d26881e96726669fee6ebb45b733 [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/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,
&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_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 <