blob: 0f19d0d8980cf0422ece4a1de3eac8e4749df325 [file] [log] [blame]
/*
*
**************************************************************************
** STMicroelectronics **
**************************************************************************
* *
* Utilities published in /proc/fts *
* *
**************************************************************************
**************************************************************************
*
*/
/*!
* \file fts_proc.c
* \brief contains the function and variables needed to publish a file node in
* the file system which allow to communicate with the IC from userspace
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include "fts_lib/fts_io.h"
#include "fts_lib/fts_flash.h"
#include "fts_lib/fts_test.h"
#include "fts_lib/fts_error.h"
#include "fts.h"
#define DRIVER_TEST_FILE_NODE "driver_test" /* /< name of file node
* published */
#define CHUNK_PROC 1024
/* /< Max chunk of a printed on the sequential file in each iteration */
/** @defgroup proc_file_code Proc File Node
* The /proc/fts/driver_test file node provide expose the most important API
* implemented into the driver to execute any possible operation into the IC \n
* Thanks to a series of Operation Codes, each of them, with a different set of
* parameter, it is possible to select a function to execute\n
* The result of the function is usually returned into the shell as an ASCII
* hex string where each byte is encoded in two chars.\n
* @{
*/
#define CMD_DRIVER_VERSION 0x00
/*!< usage: echo 00 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_WRITE 0x01
/*!< usage: to do system reset: echo 01 FA 20 00 00 24 80 >
/proc/fts/driver_test; cat /proc/fts/driver_test (raw write, no chunking) */
#define CMD_WRITEREAD 0x02
/*!< usage: to read chip ID: echo 02 FA 20 00 00 00 00 08 00
> /proc/fts/driver_test; cat /proc/fts/driver_test
(raw writeread, no chunking)*/
#define CMD_WRITEU8UX 0x03
/*!< usage: to do system reset: echo 03 FA 04 20 00 00 24 80
> /proc/fts/driver_test; cat /proc/fts/driver_test (supports chunking)*/
#define CMD_WRITEREADU8UX 0x04
/*!< usage: to read chip id: echo 04 FA 04 20 00 00 00 00 08 00 >
/proc/fts/driver_test; cat /proc/fts/driver_test (supports chunking)*/
#define CMD_READSYSINFO 0x05
/*!< usage: echo 05 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_SYSTEMRESET 0x06
/*!< usage: echo 06 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_CONFIGURESPI4 0x07
/*!< usage: echo 07 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_GETFWFILE 0x08
/*!< Not implemented */
#define CMD_FWUPDATE 0x09
/*!< usage:echo 09 00 00 00 00 > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> last 4 parameters are force
updated flags for code, reg, ms, ss respectively */
#define CMD_INTERRUPT 0x0A
/*!< usage: echo 0A 00/01 > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> 00 to disblae interrupt, 01 to enable interrupt */
#define CMD_FWREGWRITE 0x0B
/*!< usage: echo 0B 00 00 00 08 > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> first two bytes are FW address,
followed by data to be written to that address */
#define CMD_FWREGREAD 0x0C
/*!< usage: echo 0C 00 00 00 08 > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> first two bytes are FW address,
followed by number of bytes to be read from that address */
#define CMD_READMSFRAME 0x0D
/*!< usage: echo 0D ms_frame_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> MS_RAW = 0, MS_FILTER = 1,
MS_STRENGTH = 2, MS_BASELINE = 3, */
#define CMD_READSSFRAME 0x0E
/*!< usage: echo 0E ss_frame_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> SS_RAW = 0, SS_FILTER = 1,
SS_STRENGTH = 2, SS_BASELINE = 3, SS_DETECT_RAW = 4,
SS_DETECT_FILTER = 5, SS_DETECT_STRENGTH = 6, SS_DETECT_BASELINE = 7,*/
#define CMD_READSYNCFRAME 0x0F
/*!< usage: echo 0F sync_frame_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> frame type to be referred from FW API */
#define CMD_READMSCXDATA 0x10
/*!< usage: echo 10 ms_cx_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> cx type to be referred from FW API */
#define CMD_READSSCXDATA 0x11
/*!< usage: echo 11 ss_cx_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> cx type to be referred from FW API */
#define CMD_MAINTEST 0x12
/*!< usage: echo 12 00/01 00/01> /proc/fts/driver_test; cat
/proc/fts/driver_test , to enable/disable full panel init and enable/disable
test flow stop at first error*/
#define CMD_ITOTEST 0x13
/*!< usage: echo 13 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_MUTUALRAWTEST 0x14
/*!< usage: echo 14 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_MUTUALRAWLPTEST 0x15
/*!< usage: echo 15 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_SELFRAWTEST 0x16
/*!< usage: echo 16 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_SELFRAWLPTEST 0x17
/*!< usage: echo 17 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_READFIFOEVENT 0x18
/*!< usage: echo 18 > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_READSYSERRORS 0x19
/*!< usage: echo 19 > /proc/fts/driver_test; cat /proc/fts/driver_test
==> read the system errors */
#define CMD_REQUESTHDM 0x1A
/*!< usage: echo 1A hdm type > /proc/fts/driver_test; cat /proc/fts/driver_test
==> hdm type is the type of the host data memory to be loaded */
#define CMD_MUTUALTOTCXLPTEST 0x1B
/*!<usage: echo 1B 00/01> /proc/fts/driver_test; cat /proc/fts/driver_test
enable/disable test flow stop at first error*/
#define CMD_SELFTOTIXTEST 0x1C
/*!<usage: echo 1C > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_SELFTOTIXDETECTTEST 0x1D
/*!< usage: echo 1D > /proc/fts/driver_test; cat /proc/fts/driver_test */
#define CMD_READMSTOTALCXLPDATA 0x1E
/*!< usage: echo 1E ms_total_cx_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> cx type to be referred from FW API */
#define CMD_READSSTOTALIXDATA 0x1F
/*!<usage: echo 1F ss_total_ix_type > /proc/fts/driver_test;
cat /proc/fts/driver_test ==> ix type to be referred from FW API */
#define CMD_FWWRITEAUTOCLEAR 0x20
/*!<usage: echo 20 00 22 01 0F 00> /proc/fts/driver_test;
cat /proc/fts/driver_test ==> this api shall write 1 and will
be auto cleared with a time out when command is completed ,
< cmdid , 2bytes fw reg address , 1byte bitset position, 2bytes timeout in ms */
/**@}*/
#define CMD_FORCE_TOUCH_ACTIVE 0x21
/*!<usage: echo 20 _/00/01> /proc/fts/driver_test;
cat /proc/fts/driver_test ==> Prevent the driver
from transitioning the ownership of the bus to SLPI.
Single parameter indicates force touch state */
/* The string length when there is no data to print. */
#define NO_DATA_STRING_LEN 14
static int limit; /* /< store the amount of data to print into the shell*/
static int chunk; /* /< store the chuk of data that should be printed in
* this iteration */
static int printed; /* /< store the amount of data already printed in the
* shell */
static struct proc_dir_entry *fts_dir; /* /< reference to the directory
* fts under /proc */
static u8 *test_print_buff; /* /< pointer to an array of bytes used
* to store the result of the function
* executed */
char buf_chunk[CHUNK_PROC] = { 0 }; /* /< buffer used to store the message
* info received */
extern struct test_to_do tests;
/************************ SEQUENTIAL FILE UTILITIES **************************/
/**
* This function is called at the beginning of the stream to a sequential file
* or every time into the sequential were already written PAGE_SIZE bytes and
* the stream need to restart
* @param s pointer to the sequential file on which print the data
* @param pos pointer to the offset where write the data
* @return NULL if there is no data to print or the pointer to the beginning of
* the data that need to be printed
*/
static void *fts_seq_start(struct seq_file *s, loff_t *pos)
{
pr_debug("%s: Entering start(), pos = %ld limit = %d printed = %d\n",
__func__, (long)*pos, limit, printed);
if (test_print_buff == NULL && *pos == 0) {
pr_info("%s: No data to print!\n", __func__);
test_print_buff = (u8 *)kmalloc(NO_DATA_STRING_LEN * sizeof(u8), GFP_KERNEL);
if (test_print_buff == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
return NULL;
}
snprintf(test_print_buff, NO_DATA_STRING_LEN, "{ %08X }\n", ERROR_OP_NOT_ALLOW);
limit = strlen(test_print_buff);
} else {
if (*pos != 0)
*pos += chunk - 1;
if (*pos >= limit)
return NULL;
}
chunk = CHUNK_PROC;
if (limit - *pos < CHUNK_PROC)
chunk = limit - *pos;
memset(buf_chunk, 0, CHUNK_PROC);
memcpy(buf_chunk, &test_print_buff[(int)*pos], chunk);
return buf_chunk;
}
/**
* This function actually print a chunk amount of data in the sequential file
* @param s pointer to the sequential file where to print the data
* @param v pointer to the data to print
* @return 0
*/
static int fts_seq_show(struct seq_file *s, void *v)
{
pr_debug("%s: In show()\n", __func__);
seq_write(s, (u8 *)v, chunk);
printed += chunk;
return 0;
}
/**
* This function update the pointer and the counters to the next data to be
* printed
* @param s pointer to the sequential file where to print the data
* @param v pointer to the data to print
* @param pos pointer to the offset where write the next data
* @return NULL if there is no data to print or the pointer to the beginning of
* the next data that need to be printed
*/
static void *fts_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
pr_debug("%s: Entering next(), pos = %ld limit = %d printed = %d\n",
__func__, (long)*pos, limit, printed);
(*pos) += chunk;/* increase my position counter */
chunk = CHUNK_PROC;
if (*pos >= limit)
return NULL;
else if (limit - *pos < CHUNK_PROC)
chunk = limit - *pos;
memset(buf_chunk, 0, CHUNK_PROC);
memcpy(buf_chunk, &test_print_buff[(int)*pos], chunk);
return buf_chunk;
}
/**
* This function is called when there are no more data to print the stream
*need to be terminated or when PAGE_SIZE data were already written into the
*sequential file
* @param s pointer to the sequential file where to print the data
* @param v pointer returned by fts_seq_next
*/
static void fts_seq_stop(struct seq_file *s, void *v)
{
pr_debug("%s: In stop()\n", __func__);
if (v) {
pr_info("%s: v is %p.\n", __func__, v);
} else {
limit = 0;
chunk = 0;
printed = 0;
if (test_print_buff != NULL) {
kfree(test_print_buff);
test_print_buff = NULL;
}
}
}
/**
* Struct where define and specify the functions which implements the flow for
*writing on a sequential file
*/
static const struct seq_operations fts_seq_ops = {
.start = fts_seq_start,
.next = fts_seq_next,
.stop = fts_seq_stop,
.show = fts_seq_show
};
/**
* This function open a sequential file
* @param inode Inode in the file system that was called and triggered this
* function
* @param file file associated to the file node
* @return error code, 0 if success
*/
static int fts_open(struct inode *inode, struct file *file)
{
return seq_open(file, &fts_seq_ops);
};
/*****************************************************************************/
/**************************** DRIVER TEST ************************************/
/** @addtogroup proc_file_code
* @{
*/
/**
* Receive the OP code and the inputs from shell when the file node is called,
* parse it and then execute the corresponding function
* echo cmd+parameters > /proc/fts/driver_test to execute the select command
* cat /proc/fts/driver_test to obtain the result into the
* shell \n
* the string returned in the shell is made up as follow: \n
* { = start byte \n
* the answer content and format strictly depend on the cmd executed. In
* general can be: an HEX string or a byte array (e.g in case of 0xF- commands)
* \n
* } = end byte \n
*/
static ssize_t fts_seq_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
int res = OK;
char *pbuf = NULL;
char *p = NULL;
u32 *func_to_test = NULL;
u8 *cmd = NULL;
int number_param = 0;
u8 *read_buf = NULL;
u16 to_read = 0;
int index = 0;
int size = 0;
int dummy = 0;
int i = 0;
u64 address = 0;
struct mutual_sense_frame mutual_frame;
struct self_sense_frame self_frame;
struct mutual_sense_cx_data mutual_cx;
struct self_sense_cx_data self_cx;
struct force_update_flag force_burn_flag;
struct mutual_total_cx_data mutual_total_cx;
struct self_total_cx_data self_total_cx;
struct fts_ts_info *info = PDE_DATA(file_inode(file));
pbuf = (u8 *)kmalloc(count * sizeof(u8), GFP_KERNEL);
if (pbuf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
res = ERROR_ALLOC;
goto goto_end;
}
cmd = (u8 *)kmalloc(count * sizeof(u8), GFP_KERNEL);
if (cmd == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
res = ERROR_ALLOC;
goto goto_end;
}
func_to_test = (u32 *)kmalloc(((count + 1) / 3) * sizeof(u32),
GFP_KERNEL);
if (func_to_test == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
res = ERROR_ALLOC;
goto goto_end;
}
if (access_ok(buf, count) < OK ||
copy_from_user(pbuf, buf, count) != 0) {
res = ERROR_ALLOC;
goto goto_end;
}
p = pbuf;
if (((count + 1) / 3) >= 1) {
if (sscanf(p, "%02X ", &func_to_test[0]) == 1) {
p += 3;
cmd[0] = (u8)func_to_test[0];
number_param = 1;
}
} else {
res = ERROR_OP_NOT_ALLOW;
goto goto_end;
}
pr_info("%s: func_to_test[0] = %02X cmd[0]= %02X Number of Parameters = %d\n",
__func__, func_to_test[0], cmd[0], number_param);
for (; number_param < (count + 1) / 3; number_param++) {
if (sscanf(p, "%02X ",
&func_to_test[number_param]) == 1) {
p += 3;
cmd[number_param] =
(u8)func_to_test[number_param];
pr_info("%s: func_to_test[%d] = %02X cmd[%d]= %02X\n",
__func__, number_param,
func_to_test[number_param],
number_param, cmd[number_param]);
}
}
pr_info("%s: Number of Parameters = %d\n", __func__, number_param);
if (number_param >= 1) {
switch (func_to_test[0]) {
case CMD_DRIVER_VERSION:
to_read = sizeof(u32);
read_buf = (u8 *)kmalloc(to_read *
sizeof(u8), GFP_KERNEL);
if (read_buf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
to_read = 0;
res = ERROR_ALLOC;
break;
}
u32_to_u8_be(FTS_TS_DRV_VER, read_buf);
size += (to_read * sizeof(u8));
break;
case CMD_WRITE:
if (number_param >= 4)
res = fts_write(&cmd[1], number_param - 1);
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_WRITEREAD:
if (number_param >= 6) {
dummy = cmd[number_param - 1];
u8_to_u16_be(&cmd[number_param - 3], &to_read);
pr_info("%s: Number of bytes to read = %d\n",
__func__, to_read + dummy);
read_buf = (u8 *)kmalloc((to_read + dummy) *
sizeof(u8), GFP_KERNEL);
if (read_buf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
to_read = 0;
res = ERROR_ALLOC;
break;
}
res = fts_write_read(&cmd[1], number_param - 4,
read_buf, to_read + dummy);
if (res >= OK)
size += (to_read * sizeof(u8));
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_WRITEU8UX:
if (number_param >= 5) {
if (cmd[2] <= sizeof(u64)) {
u8_to_u64_be(&cmd[3], &address, cmd[2]);
pr_info("%s: address = %016llX %ld\n",
__func__, address,
(long int)address);
res = fts_write_u8ux(cmd[1], cmd[2],
address, &cmd[3 + cmd[2]],
(number_param - cmd[2] - 3));
} else {
pr_err("%s Wrong address size!\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_WRITEREADU8UX:
if (number_param >= 7) {
dummy = cmd[number_param - 1];
u8_to_u16_be(&cmd[number_param - 3], &to_read);
pr_info("%s: Number of bytes to read = %d\n",
__func__, to_read);
if (cmd[2] <= sizeof(u64)) {
u8_to_u64_be(&cmd[3], &address, cmd[2]);
pr_info("%s: address = %016llX %ld\n",
__func__, address,
(long)address);
read_buf = (u8 *)kmalloc(to_read *
sizeof(u8), GFP_KERNEL);
if (read_buf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
to_read = 0;
res = ERROR_ALLOC;
break;
}
res = fts_write_read_u8ux(cmd[1],
cmd[2], address, read_buf,
to_read, dummy);
if (res >= OK)
size += (to_read * sizeof(u8));
} else {
pr_err("%s Wrong address size!\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSYSINFO:
if (number_param == 1)
res = read_sys_info();
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_SYSTEMRESET:
if (number_param == 1) {
fts_set_interrupt(info, false);
res = fts_system_reset(info, 1);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_CONFIGURESPI4:
if (number_param == 1)
res = configure_spi4();
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_GETFWFILE:
break;
case CMD_FWUPDATE:
/*need to pass force_update_code [force_update_reg]
[forceupdate_ms] [forceupdate_ss]*/
if (number_param >= 2) {
force_burn_flag.code_update = 0;
force_burn_flag.panel_init = 0;
for (; i < FLASH_MAX_SECTIONS; i++)
force_burn_flag.section_update[i] = 0;
force_burn_flag.code_update = cmd[1];
if (number_param > 2) {
for (i = 0; i < (number_param - 2); i++)
force_burn_flag.
section_update[i] = cmd[2 + i];
}
res = flash_update(info, &force_burn_flag);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_INTERRUPT:
if (number_param == 2) {
if (cmd[1] == 1)
res = fts_set_interrupt(info, true);
else
res = fts_set_interrupt(info, false);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_FWREGWRITE:
if (number_param >= 4)
res = fts_write_fw_reg(
(u16)(((cmd[1] & 0x00FF) << 8)+
(cmd[2] & 0x00FF)), &cmd[3],
(number_param - 3));
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_FWREGREAD:
if (number_param >= 5) {
u8_to_u16_be(&cmd[3], &to_read);
read_buf = (u8 *)kmalloc(to_read * sizeof(u8),
GFP_KERNEL);
if (read_buf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
to_read = 0;
res = ERROR_ALLOC;
break;
}
res = fts_read_fw_reg(
(u16)(((cmd[1] & 0x00FF) << 8)+
(cmd[2] & 0x00FF)),
read_buf, to_read);
if (res >= OK)
size += (to_read * sizeof(u8));
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READMSFRAME:
if (number_param == 2) {
res = get_ms_frame(cmd[1], &mutual_frame);
if (res < OK)
pr_err("%s: Error while reading mutual frame..ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_short("Mutual frame =",
array_1d_to_2d_short(
mutual_frame.node_data,
mutual_frame.node_data_size,
mutual_frame.header.sense_node),
mutual_frame.header.force_node,
mutual_frame.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSSFRAME:
if (number_param == 2) {
res = get_ss_frame(cmd[1], &self_frame);
if (res < OK)
pr_err("%s: Error while reading self frame.. ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_short("Self force frame =",
array_1d_to_2d_short(
self_frame.force_data,
self_frame.header.force_node, 1),
self_frame.header.force_node, 1);
print_frame_short("Self sense frame =",
array_1d_to_2d_short(
self_frame.sense_data,
self_frame.header.sense_node,
self_frame.header.sense_node),
1,
self_frame.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSYNCFRAME:
if (number_param == 2) {
res = get_sync_frame(cmd[1], &mutual_frame,
&self_frame);
if (res < OK)
pr_err("%s: Error while reading self frame..ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_short("Mutual frame =",
array_1d_to_2d_short(
mutual_frame.node_data,
mutual_frame.node_data_size,
mutual_frame.header.sense_node),
mutual_frame.header.force_node,
mutual_frame.header.sense_node);
print_frame_short("Self force frame =",
array_1d_to_2d_short(
self_frame.force_data,
self_frame.header.force_node,
1),
self_frame.header.force_node,
1);
print_frame_short("Self sense frame =",
array_1d_to_2d_short(
self_frame.sense_data,
self_frame.header.sense_node,
self_frame.header.sense_node),
1,
self_frame.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READMSCXDATA:
if (number_param == 2) {
res = get_mutual_cx_data(cmd[1], &mutual_cx);
if (res < OK)
pr_err("%s: Error while reading mutual cx data.. "
"ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_i8("Mutual CX2 data =",
array_1d_to_2d_i8(mutual_cx.node_data,
mutual_cx.node_data_size,
mutual_cx.header.sense_node),
mutual_cx.header.force_node,
mutual_cx.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSSCXDATA:
if (number_param == 2) {
res = get_self_cx_data(cmd[1], &self_cx);
if (res < OK)
pr_err("%s: Error while reading self cx data.. "
"ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_i8("Self ix2_tx data =",
array_1d_to_2d_i8(self_cx.ix2_tx,
self_cx.header.force_node,
self_cx.header.force_node),
1,
self_cx.header.force_node);
print_frame_i8("Self cx2_tx data =",
array_1d_to_2d_i8(self_cx.cx2_tx,
self_cx.header.force_node,
self_cx.header.force_node),
1,
self_cx.header.force_node);
print_frame_i8("Self ix2_rx data =",
array_1d_to_2d_i8(self_cx.ix2_rx,
self_cx.header.sense_node,
self_cx.header.sense_node),
1,
self_cx.header.sense_node);
print_frame_i8("Self cx2_rx data =",
array_1d_to_2d_i8(self_cx.cx2_rx,
self_cx.header.sense_node,
self_cx.header.sense_node),
1,
self_cx.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_MAINTEST:
if (number_param == 3) {
res = fts_production_test_main(info,
LIMITS_FILE, cmd[2], &tests, cmd[1]);
if (res < OK)
pr_err("%s: Error running <Main> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_ITOTEST:
if (number_param == 1 || number_param == 2) {
int tries = number_param == 1 ? 1 : cmd[1];
res = ERROR_TIMEOUT;
while(res < OK && tries--) {
res = fts_production_test_ito(info, LIMITS_FILE, &tests);
if (res < OK)
pr_err("%s: Error running <ITO> tests: %08X, "
"Tries Remaining: %d\n",
__func__, res, tries);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_MUTUALRAWTEST:
if (number_param == 1) {
res = fts_production_test_ms_raw(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <MS RAW>tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_MUTUALRAWLPTEST:
if (number_param == 1) {
res = fts_production_test_ms_raw_lp(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <MS RAW LP> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_SELFRAWTEST:
if (number_param == 1) {
res = fts_production_test_ss_raw(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <SELF RAW> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_SELFRAWLPTEST:
if (number_param == 1) {
res = fts_production_test_ss_raw_lp(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <SELF RAW LP> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSYSERRORS:
if (number_param == 1)
res = fts_read_sys_errors();
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_REQUESTHDM:
if (number_param == 2)
res = fts_request_hdm(cmd[1]);
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READFIFOEVENT:
if (number_param == 1) {
to_read = 8;
dummy = 0;
read_buf = (u8 *)kmalloc(to_read * sizeof(u8),
GFP_KERNEL);
if (read_buf == NULL) {
pr_err("%s: Error allocating memory\n", __func__);
to_read = 0;
res = ERROR_ALLOC;
break;
}
res = fts_read_fw_reg(FIFO_READ_ADDR,
read_buf, to_read);
if (res >= OK)
size += (to_read * sizeof(u8));
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_MUTUALTOTCXLPTEST:
if (number_param == 2) {
res = fts_production_test_ms_cx_lp(LIMITS_FILE,
cmd[1], &tests);
if (res < OK)
pr_err("%s: Error running <MS CX LP> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_SELFTOTIXTEST:
if (number_param == 1) {
res = fts_production_test_ss_ix(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <SS IX> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_SELFTOTIXDETECTTEST:
if (number_param == 1) {
res = fts_production_test_ss_ix_lp(LIMITS_FILE,
&tests);
if (res < OK)
pr_err("%s: Error running <SS IX LP> tests: %08X\n",
__func__, res);
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READMSTOTALCXLPDATA:
if (number_param == 2) {
res = get_mutual_total_cx_data(cmd[1],
&mutual_total_cx);
if (res < OK)
pr_err("%s: Error while reading mutual total cx data.. "
"ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_short(
"Mutual Total CX data =",
array_1d_to_2d_short(
mutual_total_cx.node_data,
mutual_total_cx.node_data_size,
mutual_total_cx.header.sense_node),
mutual_total_cx.header.force_node,
mutual_total_cx.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_READSSTOTALIXDATA:
if (number_param == 2) {
res = get_self_total_cx_data(cmd[1], &self_total_cx);
if (res < OK)
pr_err("%s: Error while reading self total ix data.. "
"ERROR: %08X\n",
__func__, res);
else {
res = OK;
print_frame_u16("Self ix_tx data =",
array_1d_to_2d_u16(self_total_cx.ix_tx,
self_total_cx.header.force_node,
1),
self_total_cx.header.force_node,
1);
print_frame_u16("Self ix_rx data =",
array_1d_to_2d_u16(self_total_cx.ix_rx,
self_total_cx.header.sense_node,
self_total_cx.header.sense_node),
1,
self_total_cx.header.sense_node);
}
} else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_FWWRITEAUTOCLEAR:
if (number_param == 6)
res = fts_fw_request(
(u16)(((cmd[1] & 0x00FF) << 8)+
(cmd[2] & 0x00FF)), cmd[3], 1,
(int)((((cmd[4] & 0x00FF) << 8)+
(cmd[5] & 0x00FF))/10));
/*(actual time out in API is x10(multiple) of input)*/
else {
pr_err("%s: wrong number of parameters\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
case CMD_FORCE_TOUCH_ACTIVE:
if (number_param == 2) {
if (cmd[1] > 1) {
pr_err("%s: Parameter should be 1 or 0\n", __func__);
res = ERROR_OP_NOT_ALLOW;
} else {
pr_info("%s: FTS_FORCE_TOUCH_ACTIVE: %s\n",
__func__, cmd[1] ? "ON" : "OFF");
#if IS_ENABLED(CONFIG_GOOG_TOUCH_INTERFACE)
cmd[1] ? goog_pm_wake_lock(info->gti,
GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE, false) :
goog_pm_wake_unlock(info->gti,
GTI_PM_WAKELOCK_TYPE_FORCE_ACTIVE);
#endif
res = OK;
}
} else {
pr_err("%s: Wrong number of parameters!\n", __func__);
res = ERROR_OP_NOT_ALLOW;
}
break;
default:
pr_err("%s: COMMAND ID NOT VALID!!!\n", __func__);
res = ERROR_OP_NOT_ALLOW;
break;
}
}
goto_end:
size += (sizeof(u32)); /*for error code of 4 bytes*/
size *= 2; /*for each chars*/
size += 5; /*for "{", " ", " ", "}", "\n" */
test_print_buff = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL);
if (test_print_buff == NULL) {
pr_err("%s: Error allocating memory for io buff\n", __func__);
res = ERROR_ALLOC;
}
snprintf(&test_print_buff[index], 3, "{ ");
index += 2;
snprintf(&test_print_buff[index], 9, "%08X", res);
index += 8;
if (res >= OK) {
switch (func_to_test[0]) {
case CMD_DRIVER_VERSION:
case CMD_WRITEREAD:
case CMD_FWREGREAD:
case CMD_READFIFOEVENT:
if (dummy == 1)
i = 1;
else
i = 0;
for (; i < (to_read + dummy); i++) {
snprintf(&test_print_buff[index],
3, "%02X", read_buf[i]);
index += 2;
}
break;
}
}
snprintf(&test_print_buff[index], 4, " }\n");
limit = size;
printed = 0;
number_param = 0;
if (read_buf != NULL) {
kfree(read_buf);
read_buf = NULL;
}
if (pbuf != NULL) {
kfree(pbuf);
pbuf = NULL;
}
if (cmd != NULL) {
kfree(cmd);
cmd = NULL;
}
if (func_to_test != NULL) {
kfree(func_to_test);
func_to_test = NULL;
}
return count;
}
/** @}*/
/**
* file_operations struct which define the functions for the canonical
*operation on a device file node (open. read, write etc.)
*/
static const struct proc_ops fts_driver_test_ops = {
.proc_open = fts_open,
.proc_read = seq_read,
.proc_write = fts_seq_write,
.proc_lseek = seq_lseek,
.proc_release = seq_release
};
/**
* This function is called in the probe to initialize and create the directory
* proc/fts and the driver test file node DRIVER_TEST_FILE_NODE into the /proc
* file system
* @return OK if success or an error code which specify the type of error
*/
int fts_proc_init(struct fts_ts_info *info) {
struct proc_dir_entry *entry;
int retval = 0;
fts_dir = proc_mkdir_data("fts", 0555, NULL, info);
if (fts_dir == NULL) { /* directory creation failed */
retval = -ENOMEM;
goto out;
}
entry = proc_create_data(DRIVER_TEST_FILE_NODE, 0666, fts_dir,
&fts_driver_test_ops, info);
if (entry)
pr_info("%s: proc entry CREATED!\n", __func__);
else {
pr_err("%s: error creating proc entry!\n", __func__);
retval = -ENOMEM;
goto bad_file;
}
return OK;
bad_file:
remove_proc_entry("fts", NULL);
out:
return retval;
}
/**
* Delete and Clean from the file system, all the references to the driver test
* file node
* @return OK
*/
int fts_proc_remove(void)
{
remove_proc_entry(DRIVER_TEST_FILE_NODE, fts_dir);
remove_proc_entry("fts", NULL);
return OK;
}