| /* |
| * |
| ************************************************************************** |
| ** 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) |
| { |
| LOGD("%s: Entering start(), pos = %ld limit = %d printed = %d\n", |
| __func__, (long)*pos, limit, printed); |
| |
| if (test_print_buff == NULL && *pos == 0) { |
| LOGI("%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) { |
| LOGE("%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) |
| { |
| LOGD("%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) |
| { |
| LOGD("%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) |
| { |
| LOGD("%s: In stop()\n", __func__); |
| if (v) { |
| LOGI("%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) { |
| LOGE("%s: Error allocating memory\n", __func__); |
| res = ERROR_ALLOC; |
| goto goto_end; |
| } |
| |
| cmd = (u8 *)kmalloc(count * sizeof(u8), GFP_KERNEL); |
| if (cmd == NULL) { |
| LOGE("%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) { |
| LOGE("%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; |
| } |
| |
| LOGI("%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]; |
| LOGI("%s: func_to_test[%d] = %02X cmd[%d]= %02X\n", |
| __func__, number_param, |
| func_to_test[number_param], |
| number_param, cmd[number_param]); |
| } |
| } |
| |
| LOGI("%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) { |
| LOGE("%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 { |
| LOGE("%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); |
| LOGI("%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) { |
| LOGE("%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 { |
| LOGE("%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]); |
| LOGI("%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 { |
| LOGE("%s Wrong address size!\n", __func__); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| } else { |
| LOGE("%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); |
| LOGI("%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]); |
| LOGI("%s: address = %016llX %ld\n", |
| __func__, address, |
| (long)address); |
| read_buf = (u8 *)kmalloc(to_read * |
| sizeof(u8), GFP_KERNEL); |
| if (read_buf == NULL) { |
| LOGE("%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 { |
| LOGE("%s Wrong address size!\n", __func__); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| } else { |
| LOGE("%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 { |
| LOGE("%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 { |
| LOGE("%s: wrong number of parameters\n", __func__); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| break; |
| case CMD_CONFIGURESPI4: |
| if (number_param == 1) |
| res = configure_spi4(); |
| else { |
| LOGE("%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 { |
| LOGE("%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 { |
| LOGE("%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 { |
| LOGE("%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) { |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%s: Error running <Main> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <ITO> tests: %08X, " |
| "Tries Remaining: %d\n", |
| __func__, res, tries); |
| } |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <MS RAW>tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <MS RAW LP> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <SELF RAW> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <SELF RAW LP> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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 { |
| LOGE("%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 { |
| LOGE("%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) { |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%s: Error running <MS CX LP> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <SS IX> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%s: Error running <SS IX LP> tests: %08X\n", |
| __func__, res); |
| } else { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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) |
| LOGE("%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 { |
| LOGE("%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 { |
| LOGE("%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) { |
| LOGE("%s: Parameter should be 1 or 0\n", __func__); |
| res = ERROR_OP_NOT_ALLOW; |
| } else { |
| LOGI("%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 { |
| LOGE("%s: Wrong number of parameters!\n", __func__); |
| res = ERROR_OP_NOT_ALLOW; |
| } |
| break; |
| default: |
| LOGE("%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) { |
| LOGE("%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) |
| LOGI("%s: proc entry CREATED!\n", __func__); |
| else { |
| LOGE("%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; |
| } |