/* * ************************************************************************** ** STMicroelectronics ** ************************************************************************** ** marco.cali@st.com * ************************************************************************** * * * 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 #include #include #include #include #include #include #include #include #include #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/ftsFrame.h" #include "fts_lib/ftsFlash.h" #include "fts_lib/ftsTest.h" #include "fts_lib/ftsTime.h" #include "fts_lib/ftsTool.h" #define DRIVER_TEST_FILE_NODE "driver_test" /* /< name of file node * published */ #define CHUNK_PROC 1024 /* /< Max chunk of data printed on * the sequential file in each * iteration */ #define DIAGNOSTIC_NUM_FRAME 10 /* /< number of frames reading * iterations during the diagnostic * test */ /** @defgroup proc_file_code Proc File Node * @ingroup file_nodes * 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 * @{ */ /* Bus operations */ #define CMD_READ 0x00 /* /< I2C/SPI read: need * to pass: byteToRead1 * byteToRead0 * (optional) dummyByte * */ #define CMD_WRITE 0x01 /* /< I2C/SPI write: * need to pass: cmd[0] * cmd[1] … * cmd[cmdLength-1] */ #define CMD_WRITEREAD 0x02 /* /< I2C/SPI writeRead: * need to pass: cmd[0] * cmd[1] … * cmd[cmdLength-1] * byteToRead1 * byteToRead0 dummyByte * */ #define CMD_WRITETHENWRITEREAD 0x03 /* /< I2C/SPI write then * writeRead: need to * pass: cmdSize1 * cmdSize2 cmd1[0] * cmd1[1] … * cmd1[cmdSize1-1] * cmd2[0] cmd2[1] … * cmd2[cmdSize2-1] * byteToRead1 * byteToRead0 */ #define CMD_WRITEU8UX 0x04 /* /< I2C/SPI * writeU8UX: * need to pass: cmd * addrSize addr[0] … * addr[addrSize-1] * data[0] data[1] … */ #define CMD_WRITEREADU8UX 0x05 /* /< I2C/SPI * writeReadU8UX: need * to pass: cmd addrSize * addr[0] … * addr[addrSize-1] * byteToRead1 * byteToRead0 * hasDummyByte */ #define CMD_WRITEU8UXTHENWRITEU8UX 0x06 /* /< I2C/SPI writeU8UX * then writeU8UX: need * to pass: cmd1 * addrSize1 cmd2 * addrSize2 addr[0] … * addr[addrSize1+ * addrSize2-1] * data[0] data[1] … */ #define CMD_WRITEU8UXTHENWRITEREADU8UX 0x07 /* /< I2C/SPI writeU8UX * then writeReadU8UX: * need to pass: cmd1 * addrSize1 cmd2 * addrSize2 addr[0] … * addr[addrSize1+ * addrSize2-1] * byteToRead1 * byteToRead0 * hasDummybyte */ #define CMD_GETLIMITSFILE 0x08 /* /< Get the Production * Limits File and print * its content into the * shell: need to pass: * path(optional) * otherwise select the * approach chosen at * compile time */ #define CMD_GETFWFILE 0x09 /* /< Get the FW file * and print its content * into the shell: need * to pass: path * (optional) otherwise * select the approach * chosen at compile * time */ #define CMD_VERSION 0x0A /* /< Get the driver * version and other * driver setting info * */ #define CMD_READCONFIG 0x0B /* /< Read The config * memory, need to pass: * addr[0] addr[1] * byteToRead0 * byteToRead1 */ /* GUI utils byte ver */ #define CMD_READ_BYTE 0xF0 /* /< Byte output * version of I2C/SPI * read @see CMD_READ */ #define CMD_WRITE_BYTE 0xF1 /* /< Byte output * version of I2C/SPI * write @see CMD_WRITE * */ #define CMD_WRITEREAD_BYTE 0xF2 /* /< Byte output * version of I2C/SPI * writeRead @see * CMD_WRITEREAD */ #define CMD_WRITETHENWRITEREAD_BYTE 0xF3 /* /< Byte output * version of I2C/SPI * write then writeRead * @see * CMD_WRITETHENWRITEREAD * */ #define CMD_WRITEU8UX_BYTE 0xF4 /* /< Byte output * version of I2C/SPI * writeU8UX @see * CMD_WRITEU8UX */ #define CMD_WRITEREADU8UX_BYTE 0xF5 /* /< Byte output * version of I2C/SPI * writeReadU8UX @see * CMD_WRITEREADU8UX */ #define CMD_WRITEU8UXTHENWRITEU8UX_BYTE 0xF6 /* /< Byte output * version of I2C/SPI * writeU8UX then * writeU8UX @see * CMD_WRITEU8UXTHENWRITEU8UX * */ #define CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE 0xF7 /* /< Byte output * version of I2C/SPI * writeU8UX then * writeReadU8UX @see * CMD_WRITEU8UXTHENWRITEREADU8UX * */ #define CMD_GETLIMITSFILE_BYTE 0xF8 /* /< Byte output * version of Production * Limits File @see * CMD_GETLIMITSFILE */ #define CMD_GETFWFILE_BYTE 0xF9 /* /< Byte output * version of FW file * need to pass: @see * CMD_GETFWFILE */ #define CMD_VERSION_BYTE 0xFA /* /< Byte output * version of driver * version and setting * @see CMD_VERSION */ #define CMD_CHANGE_OUTPUT_MODE 0xFF /* /< Select the output * mode of the * scriptless protocol, * need to pass: * bin_output = 1 data * returned as binary, * bin_output =0 data * returned as hex * string */ /* Core/Tools */ #define CMD_POLLFOREVENT 0x11 /* /< Poll the FIFO for * an event: need to * pass: eventLength * event[0] event[1] … * event[eventLength-1] * timeToWait1 * timeToWait0 */ #define CMD_SYSTEMRESET 0x12 /* /< System Reset */ #define CMD_CLEANUP 0x13 /* /< Perform a system * reset and optionally * re-enable the * scanning, need to * pass: enableTouch */ #define CMD_POWERCYCLE 0x14 /* /< Execute a power * cycle toggling the * regulators */ #define CMD_READSYSINFO 0x15 /* /< Read the System * Info information from * the framebuffer, need * to pass: doRequest */ #define CMD_FWWRITE 0x16 /* /< Write a FW * command: need to * pass: cmd[0] cmd[1] * … cmd[cmdLength-1] */ #define CMD_INTERRUPT 0x17 /* /< Allow to enable or * disable the * interrupts, need to * pass: enable (if 1 * will enable the * interrupt) */ #define CMD_SETSCANMODE 0x18 /* /< set Scan Mode * need to pass: * scanType option */ #define CMD_SAVEMPFLAG 0x19 /* /< save manually a * value in the MP flag * need to pass: mpflag */ /* Frame */ #define CMD_GETFORCELEN 0x20 /* /< Get the number of * Force channels */ #define CMD_GETSENSELEN 0x21 /* /< Get the number of * Sense channels */ #define CMD_GETMSFRAME 0x23 /* /< Get a MS frame: * need to pass: * MSFrameType */ #define CMD_GETSSFRAME 0x24 /* /< Get a SS frame: * need to pass: * SSFrameType */ #define CMD_GETSYNCFRAME 0x25 /* /< Get a SS frame: * need to pass: * SSFrameType */ /* Compensation */ #define CMD_REQCOMPDATA 0x30 /* /< Request Init data: * need to pass: type */ #define CMD_READCOMPDATAHEAD 0x31 /* /< Read Init data * header: need to pass: * type */ #define CMD_READMSCOMPDATA 0x32 /* /< Read MS Init data: * need to pass: type */ #define CMD_READSSCOMPDATA 0x33 /* /< Read SS Init data: * need to pass: type */ #define CMD_READGOLDENMUTUAL 0x34 /* /< Read GoldenMutual raw data */ #define CMD_READTOTMSCOMPDATA 0x35 /* /< Read Tot MS Init * data: need to pass: * type */ #define CMD_READTOTSSCOMPDATA 0x36 /* /< Read Tot SS Init * data: need to pass: * type */ #define CMD_READSENSCOEFF 0x37 /* /< Read MS and SS * Sensitivity * Calibration * Coefficients */ /* FW Update */ #define CMD_GETFWVER 0x40 /* /< Get the FW version * of the IC */ #define CMD_FLASHUNLOCK 0x42 /* /< Unlock the flash * */ #define CMD_READFWFILE 0x43 /* /< Try to read the FW * file, need to pass: * keep_cx */ #define CMD_FLASHPROCEDURE 0x44 /* /< Perform a full * flashing procedure: * need to pass: force * keep_cx */ #define CMD_FLASHERASEUNLOCK 0x45 /* /< Unlock the erase * of the flash */ #define CMD_FLASHERASEPAGE 0x46 /* /< Erase page by page * the flash, need to * pass: keep_cx, if * keep_cx>SKIP_PANEL_INIT * Panel Init Page will * be skipped, if * >SKIP_PANEL_CX_INIT * Cx and Panel Init * Pages will be skipped * otherwise if * =ERASE_ALL all the * pages will be deleted * */ /* MP test */ #define CMD_ITOTEST 0x50 /* /< Perform an ITO * test */ #define CMD_INITTEST 0x51 /* /< Perform an * Initialization test: * need to pass: type */ #define CMD_MSRAWTEST 0x52 /* /< Perform MS raw * test: need to pass * stop_on_fail */ #define CMD_MSINITDATATEST 0x53 /* /< Perform MS Init * data test: need to * pass stop_on_fail */ #define CMD_SSRAWTEST 0x54 /* /< Perform SS raw * test: need to pass * stop_on_fail */ #define CMD_SSINITDATATEST 0x55 /* /< Perform SS Init * data test: need to * pass stop_on_fail */ #define CMD_MAINTEST 0x56 /* /< Perform a full * Mass production test: * need to pass * stop_on_fail saveInit * */ #define CMD_FREELIMIT 0x57 /* /< Free (if any) * limit file which was * loaded during any * test procedure */ /* Diagnostic */ #define CMD_DIAGNOSTIC 0x60 /* /< Perform a series * of commands and * collect severals data * to detect any * malfunction */ #define CMD_CHANGE_SAD 0x70 /* /< Allow to change * the SAD address (for * debugging) */ #define CMD_INFOBLOCK_STATUS 0x61 /* /< Check for Info * block error */ /* Debug functionalities requested by Google for B1 Project */ #define CMD_TRIGGER_FORCECAL 0x80 /* /< Trigger manually * forcecal for MS and * SS */ #define CMD_BASELINE_ADAPTATION 0x81 /* /< Enable/Disable * Baseline adaptation, * need to pass: enable * */ #define CMD_FREQ_HOP 0x82 /* /< Enable/Disable * Frequency hopping, * need to pass: enable * */ #define CMD_SET_OPERATING_FREQ 0x83 /* /< Set a defined * scanning frequency in * Hz passed as 4 bytes * in big endian, need * to pass: freq3 freq2 * freq1 freq0 */ #define CMD_READ_SYNC_FRAME 0x84 /* /< Read Sync Frame * which contain MS and * SS data, need to * pass: frameType (this * parameter can be * LOAD_SYNC_FRAME_STRENGTH * or LOAD_SYNC_FRAME_BASELINE) * */ #define CMD_TP_SENS_MODE 0x90 /* /< Enter/Exit in the * TP Sensitivity * Calibration mode, * need to pass: enter * (optional)saveGain */ #define CMD_TP_SENS_SET_SCAN_MODE 0x91 /* /< Set scan mode type * which should be used * for the test before * the stimpad is down, * need to pass: type */ #define CMD_TP_SENS_PRECAL_SS 0x92 /* /< Perform Pre * Calibration for SS * steps when stimpad is * down */ #define CMD_TP_SENS_PRECAL_MS 0x93 /* /< Perform Pre * Calibration for MS * steps when stimpad is * down */ #define CMD_TP_SENS_POSTCAL_MS 0x94 /* /< Perform Post * Calibration for MS * steps when stimpad is * down */ #define CMD_TP_SENS_STD 0x95 /* /< Compute the * Standard deviation of * a certain number of * frames, need to pass: * numFrames */ #define CMD_FORCE_TOUCH_ACTIVE 0xA0 /* /< Prevent the driver * from transitioning * the ownership of the * bus to SLPI */ static u8 bin_output; /* /< Select the output type of the scriptless * protocol (binary = 1 or hex string = 0) */ /** @}*/ /** @defgroup scriptless Scriptless Protocol * @ingroup proc_file_code * Scriptless Protocol allows ST Software (such as FingerTip Studio etc) to * communicate with the IC from an user space. * This mode gives access to common bus operations (write, read etc) and * support additional functionalities. \n * The protocol is based on exchange of binary messages included between a * start and an end byte * @{ */ #define MESSAGE_START_BYTE 0x7B /* /< start byte of each message * transferred in Scriptless Mode */ #define MESSAGE_END_BYTE 0x7D /* /< end byte of each message * transferred in Scriptless Mode */ #define MESSAGE_MIN_HEADER_SIZE 8 /* /< minimun number of bytes of the * structure of a messages exchanged * with host (include start/end byte, * counter, actions, msg_size) */ /** * Possible actions that can be requested by an host */ typedef enum { ACTION_WRITE = (u16) 0x0001, /* /< Bus Write * */ ACTION_READ = (u16) 0x0002, /* /< Bus Read * */ ACTION_WRITE_READ = (u16) 0x0003, /* /< Bus Write * followed by a * Read */ ACTION_GET_VERSION = (u16) 0x0004, /* /< Get * Version of * the protocol * (equal to the * first 2 bye * of driver * version) */ ACTION_WRITEU8UX = (u16) 0x0011, /* /< Bus Write * with support * to different * address size * */ ACTION_WRITEREADU8UX = (u16) 0x0012, /* /< Bus * writeRead * with support * to different * address size * */ ACTION_WRITETHENWRITEREAD = (u16) 0x0013, /* /< Bus write * followed by a * writeRead */ ACTION_WRITEU8XTHENWRITEREADU8UX = (u16) 0x0014, /* /< Bus write * followed by a * writeRead * with support * to different * address size * */ ACTION_WRITEU8UXTHENWRITEU8UX = (u16) 0x0015, /* /< Bus write * followed by a * write with * support to * different * address size * */ ACTION_GET_FW = (u16) 0x1000, /* /< Get Fw * file content * used by the * driver */ ACTION_GET_LIMIT = (u16) 0x1001 /* /< Get Limit * File content * used by the * driver */ } Actions; /** * Struct used to contain info of the message received by the host in * Scriptless mode */ typedef struct { u16 msg_size; /* /< total size of the message in bytes */ u16 counter; /* /< counter ID to identify a message */ Actions action; /* /< type of operation requested by the host @see * Actions */ u8 dummy; /* /< (optional)in case of any kind of read operations, * specify if the first byte is dummy */ } Message; /** @}*/ extern TestToDo tests; extern SysInfo systemInfo; 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 *driver_test_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 */ static Message mess = { 0 }; /* /< store the information of the Scriptless * message received */ /************************ 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_info("%s: Entering start(), pos = %lld limit = %d printed = %d\n", __func__, *pos, limit, printed); if (driver_test_buff == NULL && *pos == 0) { int size = 13 * sizeof(u8); pr_info("%s: No data to print!\n", __func__); driver_test_buff = (u8 *)kmalloc(size, GFP_KERNEL); limit = scnprintf(driver_test_buff, size, "{ %08X }\n", ERROR_OP_NOT_ALLOW); /* pr_err("%s: len = %d driver_test_buff = %s\n", * __func__, limit, driver_test_buff); */ } else { if (*pos != 0) *pos += chunk - 1; if (*pos >= limit) /* pr_err("%s: Apparently, we're done.\n", __func__); */ return NULL; } chunk = CHUNK_PROC; if (limit - *pos < CHUNK_PROC) chunk = limit - *pos; /* pr_err("%s: In start(), * updated pos = %Ld limit = %d printed = %d chunk = %d\n", * __func__, *pos, limit, printed, chunk); */ memset(buf_chunk, 0, CHUNK_PROC); memcpy(buf_chunk, &driver_test_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_err("%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) { /* int* val_ptr; */ /* pr_err("%s: In next(), v = %X, pos = %Ld.\n", __func__, * v, *pos); */ (*pos) += chunk;/* increase my position counter */ chunk = CHUNK_PROC; /* pr_err("%s: In next(), * updated pos = %Ld limit = %d printed = %d\n", * __func__, *pos, limit,printed); */ if (*pos >= limit) /* are we done? */ return NULL; else if (limit - *pos < CHUNK_PROC) chunk = limit - *pos; memset(buf_chunk, 0, CHUNK_PROC); memcpy(buf_chunk, &driver_test_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_err("%s: Entering stop().\n", __func__); */ if (v) { /* pr_err("%s: v is %X.\n", __func__, v); */ } else { /* pr_err("%s: v is null.\n", __func__); */ limit = 0; chunk = 0; printed = 0; if (driver_test_buff != NULL) { /* pr_err("%s: Freeing and clearing driver_test_buff.\n", * __func__); */ kfree(driver_test_buff); driver_test_buff = NULL; } else { /* pr_err("%s: driver_test_buff is already null.\n", * __func__); */ } } } /** * 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_driver_test_open(struct inode *inode, struct file *file) { struct fts_ts_info *info = dev_get_drvdata(getDev()); int retval; if (!info) { pr_err("%s: Unable to access driver data\n", __func__); retval = -ENODEV; goto exit; } if (!mutex_trylock(&info->diag_cmd_lock)) { pr_err("%s: Blocking concurrent access\n", __func__); retval = -EBUSY; goto exit; } /* Allowing only a single process to open diag procfs node */ if (info->diag_node_open == true) { pr_err("%s: Blocking multiple open\n", __func__); retval = -EBUSY; goto unlock; } retval = seq_open(file, &fts_seq_ops); if(!retval) { info->diag_node_open = true; } unlock: mutex_unlock(&info->diag_cmd_lock); exit: return retval; }; /** * This function closes 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_driver_test_release(struct inode *inode, struct file *file) { struct fts_ts_info *info = dev_get_drvdata(getDev()); int retval; if (info) mutex_lock(&info->diag_cmd_lock); else pr_err("%s: Unable to access driver data\n", __func__); retval = seq_release(inode, file); if (info) { info->diag_node_open = false; mutex_unlock(&info->diag_cmd_lock); } return retval; } /*****************************************************************************/ /**************************** 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_driver_test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { int numberParam = 0; struct fts_ts_info *info = dev_get_drvdata(getDev()); char *p = NULL; char *pbuf = NULL; char path[100] = { 0 }; int res = -1, j, index = 0; u8 report = 0; int size = 6; int temp, byte_call = 0; u16 byteToRead = 0; u32 fileSize = 0; u8 *readData = NULL; u8 *cmd = NULL; /* worst case needs count bytes */ u32 maxNum_cmd = 0; u32 *funcToTest = NULL; u32 maxNum_funcToTest = 0; u64 addr = 0; MutualSenseFrame frameMS; MutualSenseFrame deltas; SelfSenseFrame frameSS; u8 error_to_search[2] = { EVT_TYPE_ERROR_OSC_TRIM, EVT_TYPE_ERROR_AOFFSET_TRIM }; DataHeader dataHead; MutualSenseData compData; SelfSenseData comData; TotMutualSenseData totCompData; TotSelfSenseData totComData; MutualSenseCoeff msCoeff; SelfSenseCoeff ssCoeff; GoldenMutualRawData gmRawData; int meanNorm = 0, meanEdge = 0; u64 address; Firmware fw; LimitFile lim; const char *limits_file = info->board->limits_name; if (!info) { pr_err("%s: Unable to access driver data\n", __func__); count = -ENODEV; goto exit; } mess.dummy = 0; mess.action = 0; mess.msg_size = 0; 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__); if (driver_test_buff) limit = scnprintf(driver_test_buff, size, "{ %08X }\n", res); fts_set_bus_ref(info, FTS_BUS_REF_SYSFS, false); goto ERROR; } if (count < 2) { res = ERROR_OP_NOT_ALLOW; goto ERROR; } /* alloc one more space to store '\0' at the end * for "echo -n CMDs" case */ pbuf = kmalloc((count + 1) * sizeof(*pbuf), GFP_KERNEL); if (!pbuf) { res = ERROR_ALLOC; goto ERROR; } pbuf[count] = '\0'; maxNum_funcToTest = (count + 1) / 3; funcToTest = kmalloc_array(maxNum_funcToTest, sizeof(*funcToTest), GFP_KERNEL); if (!funcToTest) { res = ERROR_ALLOC; goto ERROR; } /*for(temp = 0; temp MESSAGE_MIN_HEADER_SIZE - 1 && p[0] == MESSAGE_START_BYTE) { pr_info("Enter in Byte Mode!\n"); byte_call = 1; mess.msg_size = (p[1] << 8) | p[2]; mess.counter = (p[3] << 8) | p[4]; mess.action = (p[5] << 8) | p[6]; pr_info("Message received: size = %d, counter_id = %d, action = %04X\n", mess.msg_size, mess.counter, mess.action); size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 error code */ if (count < mess.msg_size || p[count - 2] != MESSAGE_END_BYTE) { pr_err("number of byte received or end byte wrong! msg_size = %d != %zu, last_byte = %02X != %02X ... ERROR %08X\n", mess.msg_size, count, p[count - 1], MESSAGE_END_BYTE, ERROR_OP_NOT_ALLOW); res = ERROR_OP_NOT_ALLOW; goto END; } else { numberParam = mess.msg_size - MESSAGE_MIN_HEADER_SIZE + 1; /* +1 because put the internal * op code */ size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 send also * the first 2 * lsb of the * error code */ switch (mess.action) { case ACTION_READ: /* numberParam = * mess.msg_size-MESSAGE_MIN_HEADER_SIZE+1; */ cmd[0] = funcToTest[0] = CMD_READ_BYTE; break; case ACTION_WRITE: cmd[0] = funcToTest[0] = CMD_WRITE_BYTE; break; case ACTION_WRITE_READ: cmd[0] = funcToTest[0] = CMD_WRITEREAD_BYTE; break; case ACTION_GET_VERSION: cmd[0] = funcToTest[0] = CMD_VERSION_BYTE; break; case ACTION_WRITETHENWRITEREAD: cmd[0] = funcToTest[0] = CMD_WRITETHENWRITEREAD_BYTE; break; case ACTION_WRITEU8UX: cmd[0] = funcToTest[0] = CMD_WRITEU8UX_BYTE; break; case ACTION_WRITEREADU8UX: cmd[0] = funcToTest[0] = CMD_WRITEREADU8UX_BYTE; break; case ACTION_WRITEU8UXTHENWRITEU8UX: cmd[0] = funcToTest[0] = CMD_WRITEU8UXTHENWRITEU8UX_BYTE; break; case ACTION_WRITEU8XTHENWRITEREADU8UX: cmd[0] = funcToTest[0] = CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE; break; case ACTION_GET_FW: cmd[0] = funcToTest[0] = CMD_GETFWFILE_BYTE; break; case ACTION_GET_LIMIT: cmd[0] = funcToTest[0] = CMD_GETLIMITSFILE_BYTE; break; default: pr_err("Invalid Action = %d ... ERROR %08X\n", mess.action, ERROR_OP_NOT_ALLOW); res = ERROR_OP_NOT_ALLOW; goto END; } if (numberParam - 1 != 0) memcpy(&cmd[1], &p[7], numberParam - 1); /* -1 because i need to exclude the cmd[0] */ } } else { u8 result; char *token; char *path_token = NULL; size_t token_len; /* newline case at last char */ if (p[count - 1] == '\n') p[count - 1] = '\0'; /* Parse the input string to retrieve 2 hex-digit width * cmds/args separated by one or more spaces, except for * the fw/limits file name. */ while (p && numberParam < min(maxNum_cmd, maxNum_funcToTest)) { while (isspace(*p)) p++; token = strsep(&p, " "); if (!token || *token == '\0') break; token_len = strlen(token); /* break the loop to handle FW/LIMITS path */ if (numberParam == 1 && (funcToTest[0] == CMD_GETFWFILE || funcToTest[0] == CMD_GETLIMITSFILE)) { path_token = token; break; } if (token_len != 2) { pr_err("bad len. len=%zu\n", token_len); res = ERROR_OP_NOT_ALLOW; goto ERROR; } if (kstrtou8(token, 16, &result)) { /* Conversion failed due to bad input. * Discard the entire buffer. */ pr_err("bad input\n"); res = ERROR_OP_NOT_ALLOW; goto ERROR; } /* found a valid cmd/args */ cmd[numberParam] = funcToTest[numberParam] = result; pr_info("functionToTest[%d] = %02X cmd[%d] = %02X\n", numberParam, funcToTest[numberParam], numberParam, cmd[numberParam]); numberParam++; } /* FW/LIMITS path */ if (path_token && strlen(path_token)) { strlcpy(path, path_token, sizeof(path)); numberParam++; } if (numberParam == 0) { pr_err("Found invalid cmd/arg\n"); res = ERROR_OP_NOT_ALLOW; goto END; } } fw.data = NULL; lim.data = NULL; pr_info("Number of Parameters = %d\n", numberParam); /* elaborate input */ if (numberParam >= 1) { switch (funcToTest[0]) { case CMD_VERSION_BYTE: pr_info("%s: Get Version Byte\n", __func__); byteToRead = 2; mess.dummy = 0; readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); size += byteToRead; if (readData != NULL) { readData[0] = (u8)(FTS_TS_DRV_VER >> 24); readData[1] = (u8)(FTS_TS_DRV_VER >> 16); pr_info("%s: Version = %02X%02X\n", __func__, readData[0], readData[1]); res = OK; } else { res = ERROR_ALLOC; pr_err("%s: Impossible allocate memory... ERROR %08X\n", __func__, res); } break; case CMD_VERSION: byteToRead = 2 * sizeof(u32); mess.dummy = 0; readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); u32ToU8_be(FTS_TS_DRV_VER, readData); fileSize = 0; /* first two bytes bitmask of features enabled in the * IC, second two bytes bitmask of features enabled in * the driver */ #ifdef FW_H_FILE fileSize |= 0x00010000; #endif #ifdef LIMITS_H_FILE fileSize |= 0x00020000; #endif #ifdef USE_ONE_FILE_NODE fileSize |= 0x00040000; #endif #ifdef FW_UPDATE_ON_PROBE fileSize |= 0x00080000; #endif #ifdef PRE_SAVED_METHOD fileSize |= 0x00100000; #endif #ifdef COMPUTE_INIT_METHOD fileSize |= 0x00200000; #endif #ifdef USE_GESTURE_MASK fileSize |= 0x00100000; #endif #ifdef I2C_INTERFACE fileSize |= 0x00200000; #else if (getClient() && (getClient()->mode & SPI_3WIRE) == 0) fileSize |= 0x00400000; #endif #ifdef PHONE_KEY /* it is a feature enabled in the config of the chip */ fileSize |= 0x00000100; #endif #ifdef GESTURE_MODE fromIDtoMask(FEAT_SEL_GESTURE, (u8 *)&fileSize, 4); #endif #ifdef GRIP_MODE fromIDtoMask(FEAT_SEL_GRIP, (u8 *)&fileSize, 4); #endif #ifdef CHARGER_MODE fromIDtoMask(FEAT_SEL_CHARGER, (u8 *)&fileSize, 4); #endif #ifdef GLOVE_MODE fromIDtoMask(FEAT_SEL_GLOVE, (u8 *)&fileSize, 4); #endif #ifdef COVER_MODE fromIDtoMask(FEAT_SEL_COVER, (u8 *)&fileSize, 4); #endif #ifdef STYLUS_MODE fromIDtoMask(FEAT_SEL_STYLUS, (u8 *)&fileSize, 4); #endif u32ToU8_be(fileSize, &readData[4]); res = OK; size += (byteToRead * sizeof(u8)); break; case CMD_WRITEREAD: case CMD_WRITEREAD_BYTE: if (numberParam >= 5) { /* need to pass: cmd[0] cmd[1] * … cmd[cmdLength-1] * byteToRead1 byteToRead0 * dummyByte */ temp = numberParam - 4; if (cmd[numberParam - 1] == 0) mess.dummy = 0; else mess.dummy = 1; u8ToU16_be(&cmd[numberParam - 3], &byteToRead); pr_info("bytesToRead = %d\n", byteToRead + mess.dummy); readData = (u8 *)kmalloc((byteToRead + mess.dummy) * sizeof(u8), GFP_KERNEL); res = fts_writeRead_heap(&cmd[1], temp, readData, byteToRead + mess.dummy); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITE: case CMD_WRITE_BYTE: if (numberParam >= 2) { /* need to pass: cmd[0] cmd[1] * … cmd[cmdLength-1] */ temp = numberParam - 1; res = fts_write_heap(&cmd[1], temp); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READ: case CMD_READ_BYTE: if (numberParam >= 3) { /* need to pass: byteToRead1 * byteToRead0 (optional) * dummyByte */ if (numberParam == 3 || (numberParam == 4 && cmd[numberParam - 1] == 0)) mess.dummy = 0; else mess.dummy = 1; u8ToU16_be(&cmd[1], &byteToRead); readData = (u8 *)kmalloc((byteToRead + mess.dummy) * sizeof(u8), GFP_KERNEL); res = fts_read_heap(readData, byteToRead + mess.dummy); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITETHENWRITEREAD: case CMD_WRITETHENWRITEREAD_BYTE: /* need to pass: cmdSize1 cmdSize2 cmd1[0] cmd1[1] … * cmd1[cmdSize1-1] cmd2[0] cmd2[1] … cmd2[cmdSize2-1] * byteToRead1 byteToRead0 */ if (numberParam >= 6) { u8ToU16_be(&cmd[numberParam - 2], &byteToRead); readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); res = fts_writeThenWriteRead_heap( &cmd[3], cmd[1], &cmd[3 + (int)cmd[1]], cmd[2], readData, byteToRead); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITEU8UX: case CMD_WRITEU8UX_BYTE: /* need to pass: * cmd addrSize addr[0] … addr[addrSize-1] * data[0] data[1] … */ if (numberParam >= 4) { if (cmd[2] <= sizeof(u64)) { u8ToU64_be(&cmd[3], &addr, cmd[2]); pr_info("addr = %llx\n", addr); res = fts_writeU8UX(cmd[1], cmd[2], addr, &cmd[3 + cmd[2]], (numberParam - cmd[2] - 3)); } else { pr_err("Wrong address size!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITEREADU8UX: case CMD_WRITEREADU8UX_BYTE: /* need to pass: * cmd addrSize addr[0] … addr[addrSize-1] * byteToRead1 byteToRead0 hasDummyByte */ if (numberParam >= 6) { if (cmd[2] <= sizeof(u64)) { u8ToU64_be(&cmd[3], &addr, cmd[2]); u8ToU16_be(&cmd[numberParam - 3], &byteToRead); readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); pr_info("addr = %llx byteToRead = %d\n", addr, byteToRead); res = fts_writeReadU8UX(cmd[1], cmd[2], addr, readData, byteToRead, cmd[numberParam - 1]); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong address size!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITEU8UXTHENWRITEU8UX: case CMD_WRITEU8UXTHENWRITEU8UX_BYTE: /* need to pass: * cmd1 addrSize1 cmd2 addrSize2 addr[0] … * addr[addrSize1+addrSize2-1] data[0] data[1] … */ if (numberParam >= 6) { if ((cmd[2] + cmd[4]) <= sizeof(u64)) { u8ToU64_be(&cmd[5], &addr, cmd[2] + cmd[4]); pr_info("addr = %llx\n", addr); res = fts_writeU8UXthenWriteU8UX(cmd[1], cmd[2], cmd[3], cmd[4], addr, &cmd[5 + cmd[2] + cmd[4]], (numberParam - cmd[2] - cmd[4] - 5)); } else { pr_err("Wrong address size!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_WRITEU8UXTHENWRITEREADU8UX: case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE: /* need to pass: * cmd1 addrSize1 cmd2 addrSize2 addr[0] … * addr[addrSize1+addrSize2-1] byteToRead1 byteToRead0 * hasDummybyte */ if (numberParam >= 8) { if ((cmd[2] + cmd[4]) <= sizeof(u64)) { u8ToU64_be(&cmd[5], &addr, cmd[2] + cmd[4]); pr_info("%s: cmd[5] = %02X, addr = %llx\n", __func__, cmd[5], addr); u8ToU16_be(&cmd[numberParam - 3], &byteToRead); readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); res = fts_writeU8UXthenWriteReadU8UX( cmd[1], cmd[2], cmd[3], cmd[4], addr, readData, byteToRead, cmd[numberParam - 1]); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong total address size!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_CHANGE_OUTPUT_MODE: /* need to pass: bin_output */ if (numberParam >= 2) { bin_output = cmd[1]; pr_info("Setting Scriptless output mode: %d\n", bin_output); res = OK; } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FWWRITE: if (numberParam >= 3) { /* need to pass: cmd[0] cmd[1] * … cmd[cmdLength-1] */ if (numberParam >= 2) { temp = numberParam - 1; res = fts_writeFwCmd_heap(&cmd[1], temp); } else { pr_err("Wrong parameters!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_INTERRUPT: /* need to pass: enable */ if (numberParam >= 2) { if (cmd[1] == 1) res = fts_enableInterrupt(true); else res = fts_enableInterrupt(false); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_SETSCANMODE: /* need to pass: scanMode option */ if (numberParam >= 3) res = setScanMode(cmd[1], cmd[2]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_SAVEMPFLAG: /* need to pass: mpflag */ if (numberParam == 2) res = saveMpFlag(cmd[1]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READCONFIG: if (numberParam == 5) { /* need to pass: addr[0] * addr[1] byteToRead1 * byteToRead0 */ byteToRead = ((funcToTest[3] << 8) | funcToTest[4]); readData = (u8 *)kmalloc(byteToRead * sizeof(u8), GFP_KERNEL); res = readConfig((u16)((((u8)funcToTest[1] & 0x00FF) << 8) + ((u8)funcToTest[2] & 0x00FF)), readData, byteToRead); size += (byteToRead * sizeof(u8)); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_POLLFOREVENT: if (numberParam >= 5) { /* need to pass: eventLength * event[0] event[1] … * event[eventLength-1] * timeTowait1 timeTowait0 */ temp = (int)funcToTest[1]; if (numberParam == 5 + (temp - 1) && temp != 0) { readData = (u8 *)kmalloc( FIFO_EVENT_SIZE * sizeof(u8), GFP_KERNEL); res = pollForEvent( (int *)&funcToTest[2], temp, readData, ((funcToTest[temp + 2] & 0x00FF) << 8) + (funcToTest[temp + 3] & 0x00FF)); if (res >= OK) res = OK; /* pollForEvent * return the * number of * error found * */ size += (FIFO_EVENT_SIZE * sizeof(u8)); byteToRead = FIFO_EVENT_SIZE; } else { pr_err("Wrong parameters!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_SYSTEMRESET: res = fts_system_reset(); break; case CMD_READSYSINFO: if (numberParam == 2) /* need to pass: doRequest */ res = readSysInfo(funcToTest[1]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_CLEANUP:/* TOUCH ENABLE/DISABLE */ if (numberParam == 2) /* need to pass: enableTouch */ res = cleanUp(funcToTest[1]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_GETFORCELEN: /* read number Tx channels */ temp = getForceLen(); if (temp < OK) res = temp; else { size += (1 * sizeof(u8)); res = OK; } break; case CMD_GETSENSELEN: /* read number Rx channels */ temp = getSenseLen(); if (temp < OK) res = temp; else { size += (1 * sizeof(u8)); res = OK; } break; case CMD_GETMSFRAME: if (numberParam == 2) { pr_info("Get 1 MS Frame\n"); /* setScanMode(SCAN_MODE_ACTIVE, 0xFF); * 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 = getMSFrame3((MSFrameType)cmd[1], &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); size += (res * sizeof(short) + 2); /* +2 to add force and sense channels * 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 CMD_GETSSFRAME: if (numberParam == 2) { /* pr_info("Get 1 SS Frame\n"); */ /* flushFIFO(); //delete the events related to * some touch (allow to call this function while * touching the screen without having a flooding * of the FIFO) */ /* setScanMode(SCAN_MODE_ACTIVE, 0xFF); * mdelay(WAIT_FOR_FRESH_FRAMES); * setScanMode(SCAN_MODE_ACTIVE, 0x00); * mdelay(WAIT_AFTER_SENSEOFF); */ res = getSSFrame3((SSFrameType)cmd[1], &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); size += (res * sizeof(short) + 2); /* +2 to add force and sense channels * 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 CMD_GETSYNCFRAME: /* need to pass: frameType (this parameter can be * one of LOAD_SYNC_FRAME_X) */ if (numberParam == 2) { pr_info("Reading Sync Frame...\n"); res = getSyncFrame(cmd[1], &frameMS, &frameSS); if (res < OK) pr_err("Error while taking the Sync Frame frame... ERROR %08X\n", res); else { pr_info("The total frames size is %d words\n", res); size += (res * sizeof(short) + 4); /* +4 to add force and sense channels * for MS and SS. * Set res to OK because if getSyncFrame * 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); 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 CMD_REQCOMPDATA: /* request comp data */ if (numberParam == 2) { pr_info("Requesting Compensation Data\n"); res = requestHDMDownload(cmd[1]); if (res < OK) pr_err("Error requesting compensation data ERROR %08X\n", res); else pr_info("Requesting Compensation Data Finished!\n"); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READCOMPDATAHEAD: /* read comp data header */ if (numberParam == 2) { pr_info("Requesting Compensation Data\n"); res = requestHDMDownload(cmd[1]); if (res < OK) pr_err("Error requesting compensation data ERROR %08X\n", res); else { pr_info("Requesting Compensation Data Finished!\n"); res = readHDMHeader((u8)funcToTest[1], &dataHead, &address); if (res < OK) pr_err("Read Compensation Data Header ERROR %08X\n", res); else { pr_info("Read Compensation Data Header OK!\n"); size += (1 * sizeof(u8)); } } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READMSCOMPDATA:/* read mutual comp data */ if (numberParam == 2) { pr_info("Get MS Compensation Data\n"); res = readMutualSenseCompensationData(cmd[1], &compData); if (res < OK) 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 + 10) * sizeof(i8)); 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); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READSSCOMPDATA: if (numberParam == 2) { /* read self comp data */ pr_info("Get SS Compensation Data...\n"); res = readSelfSenseCompensationData(cmd[1], &comData); if (res < OK) 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 + 15) * sizeof(i8); print_frame_i8("SS Data Ix2_fm = ", array1dTo2d_i8( comData.ix2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); print_frame_i8("SS Data Cx2_fm = ", array1dTo2d_i8( comData.cx2_fm, comData.header.force_node, comData.header.force_node), 1, comData.header.force_node); print_frame_i8("SS Data Ix2_sn = ", array1dTo2d_i8( 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); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READGOLDENMUTUAL: if (numberParam != 1) { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; break; } pr_info("Get Golden Mutual Raw data\n"); res = readGoldenMutualRawData(&gmRawData); if (res < OK) { pr_err("Err reading GM data %08X\n", res); break; } pr_info("GM data reading Finished!\n"); size = (6 + gmRawData.data_size) * sizeof(u16); print_frame_short("Golden Mutual Data =", array1dTo2d_short( gmRawData.data, gmRawData.data_size, gmRawData.hdr.ms_s_len), gmRawData.hdr.ms_f_len, gmRawData.hdr.ms_s_len); break; case CMD_READTOTMSCOMPDATA: /* read mutual comp data */ if (numberParam == 2) { pr_info("Get TOT MS Compensation Data\n"); res = readTotMutualSenseCompensationData(cmd[1], &totCompData); if (res < OK) pr_err("Error reading TOT MS compensation data ERROR %08X\n", res); else { pr_info("TOT MS Compensation Data Reading Finished!\n"); size = (totCompData.node_data_size * sizeof(short) + 9); print_frame_short("MS Data (TOT Cx) =", array1dTo2d_short( totCompData.node_data, totCompData.node_data_size, totCompData.header.sense_node), totCompData.header.force_node, totCompData.header.sense_node); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READTOTSSCOMPDATA: if (numberParam == 2) { /* read self comp data */ pr_info("Get TOT SS Compensation Data...\n"); res = readTotSelfSenseCompensationData(cmd[1], &totComData); if (res < OK) pr_err("Error reading TOT SS compensation data ERROR %08X\n", res); else { pr_info("TOT SS Compensation Data Reading Finished!\n"); size = ((totComData.header.force_node + totComData.header.sense_node) * 2 * sizeof(short) + 9); print_frame_u16("SS Data TOT Ix_fm = ", array1dTo2d_u16( totComData.ix_fm, totComData.header.force_node, totComData.header.force_node), 1, totComData.header.force_node); print_frame_short( "SS Data TOT Cx_fm = ", array1dTo2d_short( totComData.cx_fm, totComData.header.force_node, totComData.header.force_node), 1, totComData.header.force_node); print_frame_u16("SS Data TOT Ix_sn = ", array1dTo2d_u16( totComData.ix_sn, totComData.header.sense_node, totComData.header.sense_node), 1, totComData.header.sense_node); print_frame_short( "SS Data TOT Cx_sn = ", array1dTo2d_short( totComData.cx_sn, totComData.header.sense_node, totComData.header.sense_node), 1, totComData.header.sense_node); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READSENSCOEFF: /* read MS and SS Sensitivity Coefficients */ pr_info("Get Sensitivity Calibration Coefficients...\n"); res = readSensitivityCoefficientsData(&msCoeff, &ssCoeff); if (res < OK) pr_err("Error reading Sensitivity Calibration Coefficients ERROR %08X\n", res); else { pr_info("Sensitivity Calibration Coefficients Reading Finished!\n"); size += (((msCoeff.node_data_size) + ssCoeff.header.force_node + ssCoeff.header.sense_node) * sizeof(u8) + 4); print_frame_u8("MS Sensitivity Coeff = ", array1dTo2d_u8(msCoeff.ms_coeff, msCoeff. node_data_size, msCoeff.header. sense_node), msCoeff.header.force_node, msCoeff.header.sense_node); print_frame_u8("SS Sensitivity Coeff force = ", array1dTo2d_u8( ssCoeff.ss_force_coeff, ssCoeff.header. force_node, 1), ssCoeff.header.force_node, 1); print_frame_u8("SS Sensitivity Coeff sense = ", array1dTo2d_u8( ssCoeff.ss_sense_coeff, ssCoeff.header. sense_node, ssCoeff.header. sense_node), 1, ssCoeff.header.sense_node); } break; case CMD_GETFWVER: size += (EXTERNAL_RELEASE_INFO_SIZE)*sizeof(u8); break; case CMD_FLASHUNLOCK: res = flash_unlock(); if (res < OK) pr_err("Impossible Unlock Flash ERROR %08X\n", res); else pr_info("Flash Unlock OK!\n"); break; case CMD_READFWFILE: if (numberParam == 2) { /* read fw file */ pr_info("Reading FW File...\n"); res = readFwFile(info->board->fw_name, &fw, funcToTest[1]); if (res < OK) pr_err("Error reading FW File ERROR %08X\n", res); else pr_info("Read FW File Finished!\n"); kfree(fw.data); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FLASHPROCEDURE: if (numberParam == 3) { /* flashing procedure */ pr_info("Starting Flashing Procedure...\n"); res = flashProcedure(info->board->fw_name, cmd[1], cmd[2]); if (res < OK) pr_err("Error during flash procedure ERROR %08X\n", res); else pr_info("Flash Procedure Finished!\n"); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FLASHERASEUNLOCK: res = flash_erase_unlock(); if (res < OK) pr_err("Error during flash erase unlock... ERROR %08X\n", res); else pr_info("Flash Erase Unlock Finished!\n"); break; case CMD_FLASHERASEPAGE: if (numberParam == 2) { /* need to pass: keep_cx */ pr_info("Reading FW File...\n"); res = readFwFile(info->board->fw_name, &fw, funcToTest[1]); if (res < OK) pr_err("Error reading FW File ERROR" "%08X\n", res); else pr_info("Read FW File Finished!\n"); pr_info("Starting Flashing Page Erase...\n"); res = flash_erase_page_by_page(cmd[1], &fw); if (res < OK) pr_err("Error during flash page erase... ERROR %08X\n", res); else pr_info("Flash Page Erase Finished!\n"); kfree(fw.data); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; /*ITO TEST*/ case CMD_ITOTEST: frameMS.node_data = NULL; res = production_test_ito(limits_file, &tests, &frameMS); if (frameMS.node_data != NULL) { size += (frameMS.node_data_size * sizeof(short) + 2); report = 1; } break; /*Initialization*/ case CMD_INITTEST: if (numberParam == 2) res = production_test_initialization(cmd[1]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_MSRAWTEST: /* MS Raw DATA TEST */ if (numberParam == 2) /* need to specify if stopOnFail * */ res = production_test_ms_raw(limits_file, cmd[1], &tests); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_MSINITDATATEST:/* MS CX DATA TEST */ if (numberParam == 2) /* need stopOnFail */ res = production_test_ms_cx(limits_file, cmd[1], &tests); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_SSRAWTEST: /* SS RAW DATA TEST */ if (numberParam == 2) /* need stopOnFail */ res = production_test_ss_raw(limits_file, cmd[1], &tests); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_SSINITDATATEST:/* SS IX CX DATA TEST */ if (numberParam == 2) /* need stopOnFail */ res = production_test_ss_ix_cx(limits_file, cmd[1], &tests); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; /*PRODUCTION TEST*/ case CMD_MAINTEST: if (numberParam >= 3) /* need to specify if stopOnFail * saveInit and * mpflag(optional) */ if (numberParam == 3) res = production_test_main(limits_file, cmd[1], cmd[2], &tests, MP_FLAG_OTHERS); else res = production_test_main(limits_file, cmd[1], cmd[2], &tests, cmd[3]); else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FREELIMIT: res = freeCurrentLimitsFile(); break; case CMD_POWERCYCLE: res = fts_chip_powercycle(info); break; case CMD_GETLIMITSFILE: /* need to pass: path(optional) return error code + * number of byte read otherwise GUI could not now how * many byte read */ if (numberParam >= 1) { lim.data = NULL; lim.size = 0; if (numberParam == 1) res = getLimitsFile(limits_file, &lim); else res = getLimitsFile(path, &lim); readData = lim.data; fileSize = lim.size; size += (fileSize * sizeof(u8)); if (byte_call == 1) size += sizeof(u32); /* transmit as * first 4 bytes * the size */ } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_GETLIMITSFILE_BYTE: /* need to pass: byteToRead1 byteToRead0 */ if (numberParam >= 3) { lim.data = NULL; lim.size = 0; u8ToU16_be(&cmd[1], &byteToRead); addr = ((u64)byteToRead) * 4; /* number of * words */ res = getLimitsFile(limits_file, &lim); readData = lim.data; fileSize = lim.size; if (fileSize > addr) { pr_err("Limits dimension expected by Host is less than actual size: expected = %d, real = %d\n", byteToRead, fileSize); res = ERROR_OP_NOT_ALLOW; } size += (addr * sizeof(u8)); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_GETFWFILE: /* need to pass: from (optional) otherwise select the * approach chosen at compile time */ if (numberParam >= 1) { if (numberParam == 1) res = getFWdata(info->board->fw_name, &readData, &fileSize); else res = getFWdata(path, &readData, &fileSize); size += (fileSize * sizeof(u8)); if (byte_call == 1) size += sizeof(u32); /* transmit as * first 4 bytes * the size */ } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_GETFWFILE_BYTE: /* need to pass: byteToRead1 byteToRead0 */ if (numberParam == 3) { u8ToU16_be(&cmd[1], &byteToRead); addr = ((u64)byteToRead) * 4; /* number of * words */ res = getFWdata(info->board->fw_name, &readData, &fileSize); if (fileSize > addr) { pr_err("FW dimension expected by Host is less than actual size: expected = %d, real = %d\n", byteToRead, fileSize); res = ERROR_OP_NOT_ALLOW; } size += (addr * sizeof(u8)); /* return always * the amount * requested by * host, if real * size is * smaller, the * data are * padded to * zero */ } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; /* finish all the diagnostic command with a goto ERROR in order * to skip the modification on driver_test_buff * remember to set properly the limit and printed variables in * order to make the seq_file logic to work */ case CMD_DIAGNOSTIC: index = 0; size = 0; fileSize = 256 * 1024 * sizeof(char); driver_test_buff = (u8 *)kzalloc(fileSize, GFP_KERNEL); readData = (u8 *)kmalloc((ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE) * sizeof(u8), GFP_KERNEL); if (driver_test_buff == NULL || readData == NULL) { res = ERROR_ALLOC; pr_err("Impossible allocate memory for buffers! ERROR %08X!\n", res); goto END; } j = scnprintf(&driver_test_buff[index], fileSize - index, "DIAGNOSTIC TEST:\n1) I2C Test: "); index += j; res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, ADDR_DCHIP_ID, (u8 *)&temp, 2, DUMMY_HW_REG); if (res < OK) { pr_err("Error during I2C test: ERROR %08X!\n", res); j = scnprintf(&driver_test_buff[index], fileSize - index, "ERROR %08X\n", res); index += j; res = ERROR_OP_NOT_ALLOW; goto END_DIAGNOSTIC; } temp &= 0xFFFF; pr_info("Chip ID = %04X!\n", temp); j = scnprintf(&driver_test_buff[index], fileSize - index, "DATA = %04X, expected = %02X%02X\n", temp, DCHIP_ID_1, DCHIP_ID_0); index += j; if (temp != ((DCHIP_ID_1 << 8) | DCHIP_ID_0)) { pr_err("Wrong CHIP ID, Diagnostic failed!\n"); res = ERROR_OP_NOT_ALLOW; goto END_DIAGNOSTIC; } j = scnprintf(&driver_test_buff[index], fileSize - index, "Present Driver Mode: %08X\n", info->mode); index += j; j = scnprintf(&driver_test_buff[index], fileSize - index, "2) FW running: Sensing On..."); index += j; pr_info("Sensing On!\n"); readData[0] = FTS_CMD_SCAN_MODE; readData[1] = SCAN_MODE_ACTIVE; readData[2] = 0x1; fts_write_heap(readData, 3); res = checkEcho(readData, 3); if (res < OK) { pr_err("No Echo received.. ERROR %08X !\n", res); j = scnprintf(&driver_test_buff[index], fileSize - index, "No echo found... ERROR %08X!\n", res); index += j; goto END_DIAGNOSTIC; } else { pr_info("Echo FOUND... OK!\n"); j = scnprintf(&driver_test_buff[index], fileSize - index, "Echo FOUND... OK!\n"); index += j; } pr_info("Reading Frames...!\n"); j = scnprintf(&driver_test_buff[index], fileSize - index, "3) Read Frames:\n"); index += j; for (temp = 0; temp < DIAGNOSTIC_NUM_FRAME; temp++) { pr_info("Iteration n. %d...\n", temp + 1); j = scnprintf(&driver_test_buff[index], fileSize - index, "Iteration n. %d...\n", temp + 1); index += j; for (addr = 0; addr < 3; addr++) { switch (addr) { case 0: j = scnprintf( &driver_test_buff[index], fileSize - index, "MS RAW FRAME ="); index += j; res |= getMSFrame3(MS_RAW, &frameMS); break; case 2: j = scnprintf( &driver_test_buff[index], fileSize - index, "MS STRENGTH FRAME ="); index += j; res |= getMSFrame3(MS_STRENGTH, &frameMS); break; case 1: j = scnprintf( &driver_test_buff[index], fileSize - index, "MS BASELINE FRAME ="); index += j; res |= getMSFrame3(MS_BASELINE, &frameMS); break; } if (res < OK) { j = scnprintf( &driver_test_buff[index], fileSize - index, "No data! ERROR %08X\n", res); index += j; } else { for (address = 0; address < frameMS.node_data_size; address++) { if (address % frameMS.header. sense_node == 0) { j = scnprintf( &driver_test_buff [index], fileSize - index, "\n"); index += j; } j = scnprintf( &driver_test_buff [index], fileSize - index, "%5d, ", frameMS. node_data[address]); index += j; } j = scnprintf( &driver_test_buff[index], fileSize - index, "\n"); index += j; } if (frameMS.node_data != NULL) kfree(frameMS.node_data); } for (addr = 0; addr < 3; addr++) { switch (addr) { case 0: j = scnprintf( &driver_test_buff[index], fileSize - index, "SS RAW FRAME =\n"); index += j; res |= getSSFrame3(SS_RAW, &frameSS); break; case 2: j = scnprintf( &driver_test_buff[index], fileSize - index, "SS STRENGTH FRAME =\n"); index += j; res |= getSSFrame3(SS_STRENGTH, &frameSS); break; case 1: j = scnprintf( &driver_test_buff[index], fileSize - index, "SS BASELINE FRAME =\n"); index += j; res |= getSSFrame3(SS_BASELINE, &frameSS); break; } if (res < OK) { j = scnprintf( &driver_test_buff[index], fileSize - index, "No data! ERROR %08X\n", res); index += j; } else { int num; short *data; num = frameSS.header.force_node; data = frameSS.force_data; for (address = 0; address < num; address++) { j = scnprintf( &driver_test_buff[index], fileSize - index, "%d\n", data[address]); index += j; } num = frameSS.header.sense_node; data = frameSS.sense_data; for (address = 0; address < num; address++) { j = scnprintf( &driver_test_buff[index], fileSize - index, "%d, ", data[address]); index += j; } j = scnprintf( &driver_test_buff[index], fileSize - index, "\n"); index += j; } if (frameSS.force_data != NULL) kfree(frameSS.force_data); if (frameSS.sense_data != NULL) kfree(frameSS.sense_data); } } pr_info("Reading error info...\n"); j = scnprintf(&driver_test_buff[index], fileSize - index, "4) FW INFO DUMP: "); index += j; temp = dumpErrorInfo(readData, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE); /* OR to detect if there are failures also in the * previous reading of frames and write the correct * result */ if (temp < OK) { pr_err("Error during dump: ERROR %08X!\n", res); j = scnprintf(&driver_test_buff[index], fileSize - index, "ERROR %08X\n", temp); index += j; } else { pr_info("DUMP OK!\n"); for (temp = 0; temp < ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE; temp++) { if (temp % ERROR_DUMP_COL_SIZE == 0) { j = scnprintf( &driver_test_buff[index], fileSize - index, "\n%2d - ", temp / ERROR_DUMP_COL_SIZE); index += j; } j = scnprintf(&driver_test_buff[index], fileSize - index, "%02X ", readData[temp]); index += j; } } res |= temp; END_DIAGNOSTIC: if (res < OK) { j = scnprintf(&driver_test_buff[index], fileSize - index, "\nRESULT = FAIL\n"); index += j; } else { j = scnprintf(&driver_test_buff[index], fileSize - index, "\nRESULT = FINISHED\n"); index += j; } /* the sting is already terminated with the null char by * scnprintf */ limit = index; printed = 0; goto ERROR; break; #ifdef I2C_INTERFACE case CMD_CHANGE_SAD: res = changeSAD(cmd[1]); break; #endif case CMD_TRIGGER_FORCECAL: cmd[0] = CAL_MS_TOUCH | CAL_SS_TOUCH; cmd[1] = 0x00; fts_enableInterrupt(false); res = writeSysCmd(SYS_CMD_FORCE_CAL, cmd, 2); res |= fts_enableInterrupt(true); if (res < OK) pr_err("can not trigger Force Cal! ERROR %08X\n", res); else pr_info("MS and SS force cal triggered!\n"); break; case CMD_BASELINE_ADAPTATION: /* need to pass: enable */ if (numberParam == 2) { if (cmd[1] == 0x01) pr_info("Enabling Baseline adaptation...\n"); else { pr_info("Disabling Baseline adaptation...\n"); cmd[1] = 0x00; /* set to zero to * disable baseline * adaptation */ } res = writeConfig(ADDR_CONFIG_AUTOCAL, &cmd[1], 1); if (res < OK) pr_err("Baseline adaptation operation FAILED! ERROR %08X\n", res); else pr_info("Baseline adaptation operation OK!\n"); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FREQ_HOP: /* need to pass: enable */ if (numberParam == 2) { pr_info("Reading MNM register...\n"); res = readConfig(ADDR_CONFIG_MNM, &cmd[2], 1); if (res < OK) { pr_err("Reading MNM register... ERROR %08X!\n", res); break; } if (cmd[1] == 0x01) { pr_info("Enabling Frequency Hopping... %02X => %02X\n", cmd[2], cmd[2] | 0x01); cmd[2] |= 0x01; /* set bit 0 to enable * Frequency Hopping */ } else { pr_info("Disabling Frequency Hopping... %02X => %02X\n", cmd[2], cmd[2] & (~0x01)); cmd[2] &= (~0x01); /* reset bit 0 * to disable * Frequency * Hopping */ } res = writeConfig(ADDR_CONFIG_MNM, &cmd[2], 1); if (res < OK) pr_err("Frequency Hopping operation FAILED! ERROR %08X\n", res); else pr_info("Frequency Hopping operation OK!\n"); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_READ_SYNC_FRAME: /* need to pass: frameType (this parameter can be * LOAD_SYNC_FRAME_STRENGTH or LOAD_SYNC_FRAME_BASELINE) * */ if (numberParam == 2) { pr_info("Reading Sync Frame...\n"); res = getSyncFrame(cmd[1], &frameMS, &frameSS); if (res < OK) pr_err("Error while taking the Sync Frame frame... ERROR %08X\n", res); else { pr_info("The total frames size is %d words\n", res); size += (res * sizeof(short) + 4); /* +4 to add force and sense channels * for MS and SS * set res to OK because if getSyncFrame * 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); 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 CMD_SET_OPERATING_FREQ: /* need to pass: freq3 freq2 freq1 freq0 */ if (numberParam == 5) { res = fts_enableInterrupt(false); if (res >= OK) { pr_info("Setting Scan Freq...\n"); u8ToU32_be(&cmd[1], &fileSize); /* fileSize is used just as container * variable, sorry for the name! */ res = setActiveScanFrequency(fileSize); if (res < OK) pr_err("Error while setting the scan frequency... ERROR %08X\n", res); else { /* setActiveScan Frequency leave * the chip in reset state but * with the new scan freq set */ /* need to enable the scan mode * and re-enable the interrupts * */ res |= setScanMode( SCAN_MODE_LOCKED, LOCKED_ACTIVE); /* this is a choice to force * the IC to use the freq set */ res |= fts_enableInterrupt( true); pr_info("Setting Scan Freq... res = %08X\n", res); } } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_MODE: /* need to pass: enter (optional)saveGain */ if (numberParam >= 2) { if (numberParam == 2) cmd[2] = 0; /* by default never save * the gain (used only * when exit) */ res = tp_sensitivity_mode(cmd[1], cmd[2]); if (res < OK) pr_err("Error while setting TP Sens mode... ERROR %08X\n", res); } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_SET_SCAN_MODE: /* need to pass: scan_type, enableGains */ if (numberParam == 3) { if (cmd[1] == 0x07) { /* To match the api for * C2/F2 */ res = tp_sensitivity_set_scan_mode( LOCKED_SINGLE_ENDED_ONLY_MUTUAL_0, cmd[2]); /* this force the IC to lock in a scan * mode */ if (res < OK) pr_err("Error while setting TP Sens scan mode... ERROR %08X\n", res); } else { pr_err("Wrong parameter!\n"); res = ERROR_OP_NOT_ALLOW; } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_PRECAL_SS: /* need to pass: target1 target0 percentage(optional) */ if (numberParam >= 3) { if (numberParam > 3) temp = cmd[3]; else temp = SENS_TEST_PERC_TARGET_PRECAL; pr_info("Setting target = %d and percentage = %d\n", (cmd[1] << 8 | cmd[2]), temp); res = tp_sensitivity_test_pre_cal_ss(&frameSS, (cmd[1] << 8 | cmd[2]), temp); if (res < OK) pr_err("Error while setting the scan frequency... ERROR %08X\n", res); if ((frameSS.force_data != NULL) && (frameSS.sense_data != NULL)) { size += ((frameSS.header.force_node + frameSS.header.sense_node) * sizeof(short) + 2); /*make error code positive to print the * frame*/ res &= (~0x80000000); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_PRECAL_MS: /* need to pass: target1 target0 calibrate * percentage(optional) */ if (numberParam >= 4) { if (numberParam > 4) temp = cmd[4]; else temp = SENS_TEST_PERC_TARGET_PRECAL; pr_info("Setting target = %d and percentage = %d\n", (cmd[1] << 8 | cmd[2]), temp); res = tp_sensitivity_test_pre_cal_ms(&frameMS, (cmd[1] << 8 | cmd[2]), temp); if (res < OK) pr_err("Error during TP Sensitivity Precal ... ERROR %08X\n", res); if (cmd[3] != 0) { pr_info("Computing gains with target = %d and saveGain = %d\n", (cmd[1] << 8 | cmd[2]), 0); temp = tp_sensitivity_compute_gains( &frameMS, (cmd[1] << 8 | cmd[2]), 0); if (temp < OK) pr_err("Error during TP Sensitivity Calibration... ERROR %08X\n", temp); res |= temp; } if (frameMS.node_data != NULL) { size += (frameMS.node_data_size * sizeof(short) + 2); /*make error code positive to print the * frame*/ res &= (~0x80000000); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_POSTCAL_MS: /* need to pass: target1 target0 executeTest * percentage(optional) */ if (numberParam >= 4) { if (cmd[3] != 0) { if (numberParam > 4) temp = cmd[4]; else temp = SENS_TEST_PERC_TARGET_POSTCAL; } else temp = -1; pr_info("Setting target = %d and percentage = %d\n", (cmd[1] << 8 | cmd[2]), temp); res = tp_sensitivity_test_post_cal_ms(&frameMS, &deltas, (cmd[1] << 8 | cmd[2]), temp, &meanNorm, &meanEdge); if (res < OK) pr_err("Error during TP Sensitivity Post Cal ... ERROR %08X\n", res); /* processing for a proper printing on the shell * */ if ((frameMS.node_data != NULL) && (deltas.node_data != NULL)) { size += ((frameMS.node_data_size + deltas.node_data_size) * sizeof(short) + 2 + 8);/* +2 force and * sense len, +8 * mean_normal/edge * */ /*make error code positive to print the * frame*/ res &= (~0x80000000); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_TP_SENS_STD: /* need to pass: numFrames */ if (numberParam >= 2) { res = tp_sensitivity_test_std_ms(cmd[1], &frameMS); if (res < OK) pr_err("Error during TP Sensitivity STD... ERROR %08X\n", res); /* processing for a proper printing on the shell * */ if (frameMS.node_data != NULL) { size += ((frameMS.node_data_size) * sizeof(short) + 2); /* +2 force and sense len */ /*make error code positive to print the * frame*/ res &= (~0x80000000); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_FORCE_TOUCH_ACTIVE: /* Single parameter indicates force touch state */ if (numberParam == 2) { if (cmd[1] > 1) { pr_err("Parameter should be 1 or 0\n"); res = ERROR_OP_NOT_ALLOW; } else { pr_info("FTS_BUS_REF_FORCE_ACTIVE: %s\n", cmd[1] ? "ON" : "OFF"); fts_set_bus_ref(info, FTS_BUS_REF_FORCE_ACTIVE, cmd[1]); res = OK; if (cmd[1]) __pm_stay_awake(&info->wakesrc); else __pm_relax(&info->wakesrc); } } else { pr_err("Wrong number of parameters!\n"); res = ERROR_OP_NOT_ALLOW; } break; case CMD_INFOBLOCK_STATUS: res = fts_system_reset(); if (res >= OK) { res = pollForErrorType(error_to_search, 2); if (res < OK) { pr_err("No info block corruption!\n"); res = OK; } else { pr_info("Info block errors found!\n"); res = ERROR_INFO_BLOCK; } } break; default: pr_err("COMMAND ID NOT VALID!!!\n"); res = ERROR_OP_NOT_ALLOW; break; } /* res2 = fts_enableInterrupt(true); * the interrupt was disabled on purpose in this node because it * can be used for testing procedure and between one step and * another the interrupt wan to be kept disabled * if (res2 < 0) { * pr_err("stm_driver_test_show: ERROR %08X\n", * (res2 | 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 */ if (driver_test_buff != NULL) { pr_info("Consecutive echo on the file node, free the buffer with the previous result\n"); kfree(driver_test_buff); } if (byte_call == 0) { size *= 2; size += 2; /* add \n and \0 (terminator char) */ } else { if (bin_output != 1) { size *= 2; /* need to code each byte as HEX string */ size -= 1; /* start byte is just one, the extra * byte of end byte taken by \n */ } else size += 1; /* add \n */ } pr_info("Size = %d\n", size); driver_test_buff = (u8 *)kzalloc(size, GFP_KERNEL); pr_info("Finish to allocate memory!\n"); if (driver_test_buff == NULL) { pr_err("Unable to allocate driver_test_buff! ERROR %08X\n", ERROR_ALLOC); goto ERROR; } if (byte_call == 0) { index = 0; index += scnprintf(&driver_test_buff[index], size - index, "{ "); index += scnprintf(&driver_test_buff[index], size - index, "%08X", res); if (res >= OK || report) { /*all the other cases are already fine printing only the * res.*/ switch (funcToTest[0]) { case CMD_VERSION: case CMD_READ: case CMD_WRITEREAD: case CMD_WRITETHENWRITEREAD: case CMD_WRITEREADU8UX: case CMD_WRITEU8UXTHENWRITEREADU8UX: case CMD_READCONFIG: case CMD_POLLFOREVENT: /* pr_err("Data = "); */ if (mess.dummy == 1) j = 1; else j = 0; for (; j < byteToRead + mess.dummy; j++) { /* pr_err("%02X ", readData[j]); */ index += scnprintf( &driver_test_buff[index], size - index, "%02X", readData[j]); /* this approach is much more faster */ } /* pr_err("\n"); */ break; case CMD_GETFWFILE: case CMD_GETLIMITSFILE: pr_info("Start To parse!\n"); for (j = 0; j < fileSize; j++) { /* pr_err("%02X ", readData[j]); */ index += scnprintf( &driver_test_buff[index], size - index, "%02X", readData[j]); } pr_info("Finish to parse!\n"); break; case CMD_GETFORCELEN: case CMD_GETSENSELEN: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)temp); break; case CMD_GETMSFRAME: case CMD_TP_SENS_PRECAL_MS: case CMD_TP_SENS_POSTCAL_MS: case CMD_TP_SENS_STD: case CMD_ITOTEST: if (frameMS.node_data == NULL) break; if (res != OK) driver_test_buff[2] = '8'; /* convert back error code to negative */ index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.sense_node); for (j = 0; j < frameMS.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameMS.node_data[j] & 0xFF00) >> 8, frameMS.node_data[j] & 0xFF); } kfree(frameMS.node_data); if (funcToTest[0] == CMD_TP_SENS_POSTCAL_MS) { /* print also mean and deltas */ for (j = 0; j < deltas.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (deltas.node_data[j] & 0xFF00) >> 8, deltas.node_data[j] & 0xFF); } kfree(deltas.node_data); index += scnprintf( &driver_test_buff[index], size - index, "%08X", meanNorm); index += scnprintf( &driver_test_buff[index], size - index, "%08X", meanEdge); } break; case CMD_GETSSFRAME: case CMD_TP_SENS_PRECAL_SS: if (res != OK) driver_test_buff[2] = '8'; /* convert back error code to negative */ index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.sense_node); /* Copying self raw data Force */ for (j = 0; j < frameSS.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.force_data[j] & 0xFF00) >> 8, frameSS.force_data[j] & 0xFF); } /* Copying self raw data Sense */ for (j = 0; j < frameSS.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.sense_data[j] & 0xFF00) >> 8, frameSS.sense_data[j] & 0xFF); } kfree(frameSS.force_data); kfree(frameSS.sense_data); break; case CMD_GETSYNCFRAME: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.sense_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.sense_node); /* Copying mutual data */ for (j = 0; j < frameMS.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameMS.node_data[j] & 0xFF00) >> 8, frameMS.node_data[j] & 0xFF); } /* Copying self data Force */ for (j = 0; j < frameSS.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.force_data[j] & 0xFF00) >> 8, frameSS.force_data[j] & 0xFF); } /* Copying self data Sense */ for (j = 0; j < frameSS.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.sense_data[j] & 0xFF00) >> 8, frameSS.sense_data[j] & 0xFF); } kfree(frameMS.node_data); kfree(frameSS.force_data); kfree(frameSS.sense_data); break; case CMD_READMSCOMPDATA: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)compData.header.type); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)compData.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)compData.header.sense_node); /* Cpying CX1 value */ index += scnprintf(&driver_test_buff[index], size - index, "%02X", compData.cx1 & 0xFF); /* Copying CX2 values */ for (j = 0; j < compData.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", compData.node_data[j] & 0xFF); } kfree(compData.node_data); break; case CMD_READSSCOMPDATA: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)comData.header.type); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.header.sense_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.f_ix1 & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.s_ix1 & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.f_cx1 & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.s_cx1 & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.f_ix0 & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%02X", comData.s_ix0 & 0xFF); /* Copying IX2 Force */ for (j = 0; j < comData.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", comData.ix2_fm[j] & 0xFF); } /* Copying IX2 Sense */ for (j = 0; j < comData.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", comData.ix2_sn[j] & 0xFF); } /* Copying CX2 Force */ for (j = 0; j < comData.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", comData.cx2_fm[j] & 0xFF); } /* Copying CX2 Sense */ for (j = 0; j < comData.header.sense_node; j++) { index += scnprintf( &driver_test_buff[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; case CMD_READGOLDENMUTUAL: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)gmRawData.hdm_hdr.type); index += scnprintf(&driver_test_buff[index], size - index, "%02X", gmRawData.hdr.ms_f_len); index += scnprintf(&driver_test_buff[index], size - index, "%02X", gmRawData.hdr.ms_s_len); index += scnprintf(&driver_test_buff[index], size - index, "%02X", gmRawData.hdr.ss_f_len); index += scnprintf(&driver_test_buff[index], size - index, "%02X", gmRawData.hdr.ss_s_len); index += scnprintf(&driver_test_buff[index], size - index, "%02X", gmRawData.hdr.ms_k_len); /* Copying Golden Mutual raw values */ for (j = 0; j < gmRawData.data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%04X", (u16)gmRawData.data[j]); } kfree(gmRawData.data); break; case CMD_READTOTMSCOMPDATA: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)totCompData.header.type); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)totCompData.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)totCompData.header.sense_node); /* Copying TOT CX values */ for (j = 0; j < totCompData.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (totCompData.node_data[j] & 0xFF00) >> 8, totCompData.node_data[j] & 0xFF); } kfree(totCompData.node_data); break; case CMD_READTOTSSCOMPDATA: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)totComData.header.type); index += scnprintf(&driver_test_buff[index], size - index, "%02X", totComData.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", totComData.header.sense_node); /* Copying TOT IX Force */ for (j = 0; j < totComData.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (totComData.ix_fm[j] & 0xFF00) >> 8, totComData.ix_fm[j] & 0xFF); } /* Copying TOT IX Sense */ for (j = 0; j < totComData.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (totComData.ix_sn[j] & 0xFF00) >> 8, totComData.ix_sn[j] & 0xFF); } /* Copying TOT CX Force */ for (j = 0; j < totComData.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (totComData.cx_fm[j] & 0xFF00) >> 8, totComData.cx_fm[j] & 0xFF); } /* Copying CX2 Sense */ for (j = 0; j < totComData.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (totComData.cx_sn[j] & 0xFF00) >> 8, totComData.cx_sn[j] & 0xFF); } kfree(totComData.ix_fm); kfree(totComData.ix_sn); kfree(totComData.cx_fm); kfree(totComData.cx_sn); break; case CMD_READSENSCOEFF: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)msCoeff.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)msCoeff.header.sense_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)ssCoeff.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)ssCoeff.header.sense_node); /* Copying MS Coefficients */ for (j = 0; j < msCoeff.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", msCoeff.ms_coeff[j] & 0xFF); } /* Copying SS force Coefficients */ for (j = 0; j < ssCoeff.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", ssCoeff.ss_force_coeff[j] & 0xFF); } /* Copying SS sense Coefficients */ for (j = 0; j < ssCoeff.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", ssCoeff.ss_sense_coeff[j] & 0xFF); } kfree(msCoeff.ms_coeff); kfree(ssCoeff.ss_force_coeff); kfree(ssCoeff.ss_sense_coeff); break; case CMD_GETFWVER: for (j = 0; j < EXTERNAL_RELEASE_INFO_SIZE; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X", systemInfo.u8_releaseInfo[j]); } break; case CMD_READCOMPDATAHEAD: index += scnprintf(&driver_test_buff[index], size - index, "%02X", dataHead.type); break; case CMD_READ_SYNC_FRAME: index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameMS.header.sense_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.force_node); index += scnprintf(&driver_test_buff[index], size - index, "%02X", (u8)frameSS.header.sense_node); /* Copying mutual data */ for (j = 0; j < frameMS.node_data_size; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameMS.node_data[j] & 0xFF00) >> 8, frameMS.node_data[j] & 0xFF); } /* Copying self data Force */ for (j = 0; j < frameSS.header.force_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.force_data[j] & 0xFF00) >> 8, frameSS.force_data[j] & 0xFF); } /* Copying self data Sense */ for (j = 0; j < frameSS.header.sense_node; j++) { index += scnprintf( &driver_test_buff[index], size - index, "%02X%02X", (frameSS.sense_data[j] & 0xFF00) >> 8, frameSS.sense_data[j] & 0xFF); } kfree(frameMS.node_data); kfree(frameSS.force_data); kfree(frameSS.sense_data); break; default: break; } } index += scnprintf(&driver_test_buff[index], size - index, " }\n"); limit = size - 1;/* avoid to print \0 in the shell */ printed = 0; } else { /* start byte */ driver_test_buff[index++] = MESSAGE_START_BYTE; if (bin_output == 1) { /* msg_size */ driver_test_buff[index++] = (size & 0xFF00) >> 8; driver_test_buff[index++] = (size & 0x00FF); /* counter id */ driver_test_buff[index++] = (mess.counter & 0xFF00) >> 8; driver_test_buff[index++] = (mess.counter & 0x00FF); /* action */ driver_test_buff[index++] = (mess.action & 0xFF00) >> 8; driver_test_buff[index++] = (mess.action & 0x00FF); /* error */ driver_test_buff[index++] = (res & 0xFF00) >> 8; driver_test_buff[index++] = (res & 0x00FF); } else { if (funcToTest[0] == CMD_GETLIMITSFILE_BYTE || funcToTest[0] == CMD_GETFWFILE_BYTE) index += scnprintf(&driver_test_buff[index], size - index, "%02X%02X", (((fileSize + 3) / 4) & 0xFF00) >> 8, ((fileSize + 3) / 4) & 0x00FF); else index += scnprintf(&driver_test_buff[index], size - index, "%02X%02X", (size & 0xFF00) >> 8, size & 0xFF); index += scnprintf(&driver_test_buff[index], size - index, "%04X", (u16)mess.counter); index += scnprintf(&driver_test_buff[index], size - index, "%04X", (u16)mess.action); index += scnprintf(&driver_test_buff[index], size - index, "%02X%02X", (res & 0xFF00) >> 8, res & 0xFF); } switch (funcToTest[0]) { case CMD_VERSION_BYTE: case CMD_READ_BYTE: case CMD_WRITEREAD_BYTE: case CMD_WRITETHENWRITEREAD_BYTE: case CMD_WRITEREADU8UX_BYTE: case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE: if (bin_output == 1) { if (mess.dummy == 1) memcpy(&driver_test_buff[index], &readData[1], byteToRead); else memcpy(&driver_test_buff[index], readData, byteToRead); index += byteToRead; } else { j = mess.dummy; for (; j < byteToRead + mess.dummy; j++) index += scnprintf( &driver_test_buff[index], size - index, "%02X", (u8)readData[j]); } break; case CMD_GETLIMITSFILE_BYTE: case CMD_GETFWFILE_BYTE: if (bin_output == 1) { /* override the msg_size with dimension in words * */ driver_test_buff[1] = ( ((fileSize + 3) / 4) & 0xFF00) >> 8; driver_test_buff[2] = ( ((fileSize + 3) / 4) & 0x00FF); if (readData != NULL) memcpy(&driver_test_buff[index], readData, fileSize); else pr_err("readData = NULL... returning junk data!"); index += addr; /* in this case the byte to read * are stored in addr because it * is a u64 end byte need to be * inserted at the end of the * padded memory */ } else { /* snprintf(&driver_test_buff[1], 3, "%02X", * (((fileSize + 3) / 4)&0xFF00) >> 8); */ /* snprintf(&driver_test_buff[3], 3, "%02X", * ((fileSize + 3) / 4)&0x00FF); */ for (j = 0; j < fileSize; j++) index += scnprintf( &driver_test_buff[index], size - index, "%02X", (u8)readData[j]); for (; j < addr; j++) index += scnprintf( &driver_test_buff[index], size - index, "%02X", 0); /* pad memory * with 0x00 */ } break; default: break; } index += scnprintf(&driver_test_buff[index], size - index, "%c\n", MESSAGE_END_BYTE); /*for(j=0; j