/* * Omnivision TCM touchscreen driver * * Copyright (C) 2017-2018 Omnivision Incorporated. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND Omnivision * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. * IN NO EVENT SHALL Omnivision BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF Omnivision WAS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, Omnivision' * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. * DOLLARS. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "omnivision_tcm_core.h" #include "omnivision_tcm_testing.h" #define SYSFS_DIR_NAME "testing" #define OVT_TCM_LIMIT_TSR_IMAGE_NAME "omnivision/tsr_limit.img" #define REPORT_TIMEOUT_MS 5000 #define testing_sysfs_show(t_name) \ static ssize_t testing_sysfs_##t_name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ int retval; \ struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; \ \ mutex_lock(&tcm_hcd->extif_mutex); \ \ retval = testing_##t_name(); \ if (retval < 0) { \ LOGE(tcm_hcd->pdev->dev.parent, \ "Failed to do "#t_name" test\n"); \ goto exit; \ } \ \ retval = snprintf(buf, PAGE_SIZE, \ "%s\n", \ testing_hcd->result ? "Passed" : "Failed"); \ \ exit: \ mutex_unlock(&tcm_hcd->extif_mutex); \ \ return retval; \ } #define testing_sysfs_raw_delta_show(t_name) \ static ssize_t testing_sysfs_##t_name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ int retval; \ int count = 0; \ int rows, cols, i, j; \ int data_value; \ unsigned char *report_data_buf; \ struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; \ struct ovt_tcm_app_info *app_info; \ \ mutex_lock(&tcm_hcd->extif_mutex); \ \ retval = testing_##t_name(); \ if (retval < 0) { \ LOGE(tcm_hcd->pdev->dev.parent, \ "Failed to do "#t_name" test\n"); \ goto exit; \ } \ \ count = 0; \ \ retval = snprintf(buf, PAGE_SIZE - count, \ "#t_name data: %d\n", \ tcm_hcd->id_info.version); \ buf += retval; \ count += retval; \ \ app_info = &tcm_hcd->app_info; \ rows = le2_to_uint(app_info->num_of_image_rows); \ cols = le2_to_uint(app_info->num_of_image_cols); \ report_data_buf = testing_hcd->report.buf; \ data_value = 0; \ for (i = 0; i < rows; i++) { \ for (j = 0; j < cols; j++) { \ data_value = (short)le2_to_uint(&report_data_buf[(i * cols + j) * 2]); \ retval = snprintf(buf, PAGE_SIZE - count,"%04d, ", data_value); \ buf += retval; \ count += retval; \ } \ retval = snprintf(buf, PAGE_SIZE - count,"\n"); \ buf += retval; \ count += retval; \ } \ \ retval = snprintf(buf, PAGE_SIZE - count,"\n"); \ buf += retval; \ count += retval; \ \ exit: \ mutex_unlock(&tcm_hcd->extif_mutex); \ \ return count; \ } #define CHECK_BIT(var, pos) ((var) & (1<<(pos))) /* Sync with the Comm2 Release 21 */ /* Not all are implemented for every ASIC */ enum test_code { TEST_NOT_IMPLEMENTED = 0x00, TEST_PT1_TRX_TRX_SHORTS = 0x01, TEST_PT2_TRX_SENSOR_OPENS = 0x02, TEST_PT3_TRX_GROUND_SHORTS = 0x03, TEST_PT5_FULL_RAW_CAP = 0x05, TEST_PT6_EE_SHORT = 0x06, TEST_PT7_DYNAMIC_RANGE = 0x07, TEST_PT8_HIGH_RESISTANCE = 0x08, TEST_PT10_DELTA_NOISE = 0x0a, TEST_PT11_OPEN_DETECTION = 0x0b, TEST_PT12 = 0x0c, TEST_PT13 = 0x0d, TEST_PT14_DOZE_DYNAMIC_RANGE = 0x0e, TEST_PT15_DOZE_NOISE = 0x0f, TEST_PT16_SENSOR_SPEED = 0x10, TEST_PT17_ADC_RANGE = 0x11, TEST_PT18_HYBRID_ABS_RAW = 0x12, TEST_PT29_HYBRID_ABS_NOISE = 0x1D, }; struct testing_hcd { bool result; unsigned char report_type; unsigned int report_index; unsigned int num_of_reports; struct kobject *sysfs_dir; struct proc_dir_entry *proc_csv; struct ovt_tcm_buffer out; struct ovt_tcm_buffer resp; struct ovt_tcm_buffer report; struct ovt_tcm_buffer process; struct ovt_tcm_buffer output; struct ovt_tcm_hcd *tcm_hcd; struct ovt_tcm_test_threshold testing_csv_threshold; int (*collect_reports)(enum report_type report_type, unsigned int num_of_reports); const struct firmware *limit_tsr_fw_entry; #ifdef PT1_GET_PIN_ASSIGNMENT #define MAX_PINS (64) unsigned char *satic_cfg_buf; short tx_pins[MAX_PINS]; short tx_assigned; short rx_pins[MAX_PINS]; short rx_assigned; short guard_pins[MAX_PINS]; short guard_assigned; #endif }; DECLARE_COMPLETION(report_complete); DECLARE_COMPLETION(testing_remove_complete); static struct testing_hcd *testing_hcd; /* testing implementation */ static int testing_device_id(void); static int testing_config_id(void); static int testing_reset_open(void); static int testing_pt01_trx_trx_short(void); static int testing_pt05_full_raw(void); static int testing_pt07_dynamic_range(void); static int testing_pt10_noise(void); static int testing_do_testing(void); static int testing_pt11_open_detection(void); static int testing_raw_data(void); static int testing_delta_data(void); SHOW_PROTOTYPE(testing, testing_size) /* nodes for testing */ SHOW_PROTOTYPE(testing, device_id) SHOW_PROTOTYPE(testing, config_id) SHOW_PROTOTYPE(testing, reset_open) SHOW_PROTOTYPE(testing, pt01_trx_trx_short) SHOW_PROTOTYPE(testing, pt05_full_raw) SHOW_PROTOTYPE(testing, pt07_dynamic_range) SHOW_PROTOTYPE(testing, pt10_noise) SHOW_PROTOTYPE(testing, do_testing) SHOW_PROTOTYPE(testing, pt11_open_detection) SHOW_PROTOTYPE(testing, raw_data) SHOW_PROTOTYPE(testing, delta_data) static struct device_attribute *attrs[] = { ATTRIFY(testing_size), ATTRIFY(device_id), ATTRIFY(config_id), ATTRIFY(pt01_trx_trx_short), ATTRIFY(pt05_full_raw), ATTRIFY(pt07_dynamic_range), ATTRIFY(pt10_noise), ATTRIFY(pt11_open_detection), ATTRIFY(do_testing), ATTRIFY(reset_open), ATTRIFY(raw_data), ATTRIFY(delta_data), }; static ssize_t testing_sysfs_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); static struct bin_attribute bin_attr = { .attr = { .name = "data", .mode = S_IRUGO, }, .size = 0, .read = testing_sysfs_data_show, }; testing_sysfs_show(device_id) testing_sysfs_show(config_id) testing_sysfs_show(pt01_trx_trx_short) testing_sysfs_show(pt05_full_raw) testing_sysfs_show(pt07_dynamic_range) testing_sysfs_show(pt10_noise) testing_sysfs_show(pt11_open_detection) testing_sysfs_show(reset_open) testing_sysfs_raw_delta_show(raw_data) testing_sysfs_raw_delta_show(delta_data) //#define LIMIT_FROM_CSV_FILE 1 #ifdef LIMIT_FROM_CSV_FILE static int ovt_tcm_get_thr_from_csvfile(void) { int ret = 0; unsigned int rows = 0; unsigned int cols = 0; struct ovt_tcm_app_info *app_info = NULL; struct ovt_tcm_test_threshold *threshold = &(testing_hcd->testing_csv_threshold); struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; char file_path[256] = {0}; strncpy(file_path, "/data/ovt_tcm_", sizeof(file_path)); // if (tcm_hcd->hw_if->bdata->project_id) { // strncat(file_path, tcm_hcd->hw_if->bdata->project_id, sizeof(file_path)); // } strncat(file_path, "cap_limits.csv", sizeof(file_path) - strlen(file_path) - 1); app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); printk("ovt tcm csv parser: rows: %d, cols: %d file_path:%s", rows, cols, file_path); ret = ovt_tcm_parse_csvfile(file_path, CSV_RAW_DATA_MIN_ARRAY, threshold->raw_data_min_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_RAW_DATA_MIN_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_RAW_DATA_MAX_ARRAY, threshold->raw_data_max_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_RAW_DATA_MAX_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_OPEN_SHORT_MIN_ARRAY, threshold->open_short_min_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_OPEN_SHORT_MIN_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_OPEN_SHORT_MAX_ARRAY, threshold->open_short_max_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_OPEN_SHORT_MAX_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_LCD_NOISE_ARRAY, threshold->lcd_noise_max_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_LCD_NOISE_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_PT17_MAX_ARRAY, threshold->pt17_max_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_PT17_MAX_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_PT18_MIN_ARRAY, threshold->pt18_min_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_PT18_MIN_ARRAY); return ret; } ret = ovt_tcm_parse_csvfile(file_path, CSV_PT18_MAX_ARRAY, threshold->pt18_max_limits, rows, cols); if (ret) { printk("ovt tcm csv parser: %s: Failed get %s \n", __func__, CSV_PT18_MAX_ARRAY); return ret; } printk("ovt tcm csv parser: %s: success \n", __func__); return 0; } #endif static char *g_testing_output_buf; #define OUTPUT_TO_CSV_STRING_LEN 100*1024 static ssize_t testing_sysfs_do_testing_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; int test_result; //struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; //mutex_lock(&tcm_hcd->extif_mutex); test_result = testing_do_testing(); if (test_result) { retval = snprintf(buf, PAGE_SIZE, "%s\n","fail"); } else { retval = snprintf(buf, PAGE_SIZE, "%s\n","pass"); } return retval; } static ssize_t testing_sysfs_testing_size_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; mutex_lock(&tcm_hcd->extif_mutex); LOCK_BUFFER(testing_hcd->output); retval = snprintf(buf, PAGE_SIZE, "%u\n", testing_hcd->output.data_length); UNLOCK_BUFFER(testing_hcd->output); mutex_unlock(&tcm_hcd->extif_mutex); return retval; } static ssize_t testing_sysfs_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) { int retval; unsigned int readlen; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; mutex_lock(&tcm_hcd->extif_mutex); LOCK_BUFFER(testing_hcd->output); readlen = MIN(count, testing_hcd->output.data_length - pos); retval = secure_memcpy(buf, count, &testing_hcd->output.buf[pos], testing_hcd->output.buf_size - pos, readlen); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to copy report data\n"); } else { retval = readlen; } UNLOCK_BUFFER(testing_hcd->output); mutex_unlock(&tcm_hcd->extif_mutex); return retval; } /*/proc/fts_test_csv*/ static int ovt_csv_show(struct seq_file *s, void *v) { seq_printf(s, "%s", g_testing_output_buf); return 0; } static int ovt_csv_open(struct inode *inode, struct file *file) { return single_open(file, ovt_csv_show, PDE_DATA(inode)); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) static const struct proc_ops ovt_proccsv_fops = { .proc_open = ovt_csv_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, }; #else static const struct file_operations ovt_proccsv_fops = { .open = ovt_csv_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif static int testing_run_prod_test_item(enum test_code test_code) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; if (tcm_hcd->features.dual_firmware && tcm_hcd->id_info.mode != MODE_PRODUCTIONTEST_FIRMWARE) { retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_PRODUCTION_TEST); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run production test firmware\n"); return retval; } } else if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || tcm_hcd->app_status != APP_STATUS_OK) { LOGE(tcm_hcd->pdev->dev.parent, "Identifying mode = 0x%02x\n", tcm_hcd->id_info.mode); return -ENODEV; } LOCK_BUFFER(testing_hcd->out); retval = ovt_tcm_alloc_mem(tcm_hcd, &testing_hcd->out, 1); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for testing_hcd->out.buf\n"); UNLOCK_BUFFER(testing_hcd->out); return retval; } testing_hcd->out.buf[0] = test_code; LOCK_BUFFER(testing_hcd->resp); retval = tcm_hcd->write_message(tcm_hcd, CMD_PRODUCTION_TEST, testing_hcd->out.buf, 1, &testing_hcd->resp.buf, &testing_hcd->resp.buf_size, &testing_hcd->resp.data_length, NULL, 0); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to write command %s\n", STR(CMD_PRODUCTION_TEST)); UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); return retval; } UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); return 0; } static int testing_collect_reports(enum report_type report_type, unsigned int num_of_reports) { int retval; bool completed; unsigned int timeout; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; testing_hcd->report_index = 0; testing_hcd->report_type = report_type; testing_hcd->num_of_reports = num_of_reports; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) reinit_completion(&report_complete); #else INIT_COMPLETION(report_complete); #endif LOCK_BUFFER(testing_hcd->out); retval = ovt_tcm_alloc_mem(tcm_hcd, &testing_hcd->out, 1); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for testing_hcd->out.buf\n"); UNLOCK_BUFFER(testing_hcd->out); goto exit; } testing_hcd->out.buf[0] = testing_hcd->report_type; LOCK_BUFFER(testing_hcd->resp); retval = tcm_hcd->write_message(tcm_hcd, CMD_ENABLE_REPORT, testing_hcd->out.buf, 1, &testing_hcd->resp.buf, &testing_hcd->resp.buf_size, &testing_hcd->resp.data_length, NULL, 0); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to write command %s\n", STR(CMD_ENABLE_REPORT)); UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); goto exit; } UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); completed = false; timeout = REPORT_TIMEOUT_MS * num_of_reports; retval = wait_for_completion_timeout(&report_complete, msecs_to_jiffies(timeout)); if (retval == 0) { LOGE(tcm_hcd->pdev->dev.parent, "Timed out waiting for report collection\n"); } else { completed = true; } LOCK_BUFFER(testing_hcd->out); testing_hcd->out.buf[0] = testing_hcd->report_type; LOCK_BUFFER(testing_hcd->resp); retval = tcm_hcd->write_message(tcm_hcd, CMD_DISABLE_REPORT, testing_hcd->out.buf, 1, &testing_hcd->resp.buf, &testing_hcd->resp.buf_size, &testing_hcd->resp.data_length, NULL, 0); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to write command %s\n", STR(CMD_DISABLE_REPORT)); UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); goto exit; } UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); if (completed) retval = 0; else retval = -EIO; exit: testing_hcd->report_type = 0; return retval; } static void testing_get_frame_size_words(unsigned int *size, bool image_only) { unsigned int rows; unsigned int cols; unsigned int hybrid; unsigned int buttons; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); hybrid = le2_to_uint(app_info->has_hybrid_data); buttons = le2_to_uint(app_info->num_of_buttons); *size = rows * cols; if (!image_only) { if (hybrid) *size += rows + cols; *size += buttons; } return; } static void testing_standard_frame_output(bool image_only) { int retval; unsigned int data_size; unsigned int header_size; unsigned int output_size; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; app_info = &tcm_hcd->app_info; testing_get_frame_size_words(&data_size, image_only); header_size = sizeof(app_info->num_of_buttons) + sizeof(app_info->num_of_image_rows) + sizeof(app_info->num_of_image_cols) + sizeof(app_info->has_hybrid_data); output_size = header_size + data_size * 2; LOCK_BUFFER(testing_hcd->output); retval = ovt_tcm_alloc_mem(tcm_hcd, &testing_hcd->output, output_size); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for testing_hcd->output.buf\n"); UNLOCK_BUFFER(testing_hcd->output); return; } retval = secure_memcpy(testing_hcd->output.buf, testing_hcd->output.buf_size, &app_info->num_of_buttons[0], header_size, header_size); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to copy header data\n"); UNLOCK_BUFFER(testing_hcd->output); return; } output_size = header_size; LOCK_BUFFER(testing_hcd->resp); retval = secure_memcpy(testing_hcd->output.buf + header_size, testing_hcd->output.buf_size - header_size, testing_hcd->resp.buf, testing_hcd->resp.buf_size, testing_hcd->resp.data_length); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to copy test data\n"); UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->output); return; } output_size += testing_hcd->resp.data_length; UNLOCK_BUFFER(testing_hcd->resp); testing_hcd->output.data_length = output_size; UNLOCK_BUFFER(testing_hcd->output); return; } static const char *device_id_limit = "3618"; static int testing_device_id(void) { struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; struct ovt_tcm_identification *id_info; char *strptr = NULL; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; id_info = &tcm_hcd->id_info; strptr = strnstr(id_info->part_number, device_id_limit, sizeof(id_info->part_number)); if (strptr != NULL) testing_hcd->result = true; else LOGE(tcm_hcd->pdev->dev.parent, "Device ID is mismatching, FW: %s (%s)\n", id_info->part_number, device_id_limit); LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return 0; } static int testing_config_id(void) { struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; struct ovt_tcm_app_info *app_info; int i; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; testing_hcd->result = true; for (i = 0; i < sizeof(config_id_limit); i++) { if (config_id_limit[i] != tcm_hcd->app_info.customer_config_id[i]) { LOGE(tcm_hcd->pdev->dev.parent, "Config ID is mismatching at byte %d\n", i); testing_hcd->result = false; } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return 0; } #ifdef PT1_GET_PIN_ASSIGNMENT static int testing_get_static_config(unsigned char *buf, unsigned int buf_len) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; if (!buf) { LOGE(tcm_hcd->pdev->dev.parent, "invalid parameter\n"); return -EINVAL; } LOCK_BUFFER(testing_hcd->out); LOCK_BUFFER(testing_hcd->resp); retval = tcm_hcd->write_message(tcm_hcd, CMD_GET_STATIC_CONFIG, NULL, 0, &testing_hcd->resp.buf, &testing_hcd->resp.buf_size, &testing_hcd->resp.data_length, NULL, 0); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to write command %s\n", STR(CMD_GET_STATIC_CONFIG)); goto exit; } if (testing_hcd->resp.data_length != buf_len) { LOGE(tcm_hcd->pdev->dev.parent, "Cfg size mismatch\n"); retval = -EINVAL; goto exit; } retval = secure_memcpy(buf, buf_len, testing_hcd->resp.buf, testing_hcd->resp.buf_size, buf_len); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to copy cfg data\n"); goto exit; } exit: UNLOCK_BUFFER(testing_hcd->resp); UNLOCK_BUFFER(testing_hcd->out); return retval; } static bool testing_is_pins_assigned(int pin) { int i; short *tx_pins = testing_hcd->tx_pins; short tx_assigned = testing_hcd->tx_assigned; short *rx_pins = testing_hcd->rx_pins; short rx_assigned = testing_hcd->rx_assigned; short *guard_pins = testing_hcd->guard_pins; short guard_assigned = testing_hcd->guard_assigned; for (i = 0; i < tx_assigned; i++) { if (pin == tx_pins[i]) return true; } for (i = 0; i < rx_assigned; i++) { if (pin == rx_pins[i]) return true; } for (i = 0; i < guard_assigned; i++) { if (pin == guard_pins[i]) return true; } return false; } static int testing_get_pins_mapping(unsigned char *cfg_data, int length) { struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; int i, j = 0; int idx; int offset_tx_pin = cfg_imagetxes.offset/8; int length_tx_pin = cfg_imagetxes.length/8; int offset_rx_pin = cfg_imagerxes.offset/8; int length_rx_pin = cfg_imagerxes.length/8; int num_tx_guard = 0; int offset_num_tx_guard = cfg_numtxguards.offset/8; int length_num_tx_guard = cfg_numtxguards.length/8; int offset_tx_guard = cfg_txguardpins.offset/8; int length_tx_guard = cfg_txguardpins.length/8; int num_rx_guard = 0; int offset_num_rx_guard = cfg_numrxguards.offset/8; int length_num_rx_guard = cfg_numrxguards.length/8; int offset_rx_guard = cfg_rxguardpins.offset/8; int length_rx_guard = cfg_rxguardpins.length/8; if (!cfg_data) { LOGE(tcm_hcd->pdev->dev.parent, "invalid parameter\n"); return -EINVAL; } testing_hcd->tx_assigned = 0; testing_hcd->rx_assigned = 0; testing_hcd->guard_assigned = 0; /* get tx pins mapping */ if (0 == (offset_tx_pin + length_tx_pin)) goto get_rx_pins; if (length > offset_tx_pin + length_tx_pin) { testing_hcd->tx_assigned = (length_tx_pin/2); idx = 0; for (i = 0; i < (length_tx_pin/2); i++) { testing_hcd->tx_pins[i] = (short)cfg_data[offset_tx_pin + idx] | (short)(cfg_data[offset_tx_pin + idx + 1] << 8); idx += 2; LOGD(tcm_hcd->pdev->dev.parent, "tx[%d] = %2d\n", i, testing_hcd->tx_pins[i]); } } get_rx_pins: /* get rx pins mapping */ if (0 == (offset_rx_pin + length_rx_pin)) goto get_num_tx_guards; if (length > offset_rx_pin + length_rx_pin) { testing_hcd->rx_assigned = (length_rx_pin/2); idx = 0; for (i = 0; i < (length_rx_pin/2); i++) { testing_hcd->rx_pins[i] = (short)cfg_data[offset_rx_pin + idx] | (short)(cfg_data[offset_rx_pin + idx + 1] << 8); idx += 2; LOGD(tcm_hcd->pdev->dev.parent, "rx[%d] = %2d\n", i, testing_hcd->rx_pins[i]); } } get_num_tx_guards: /* get number of tx guards */ if (0 == (offset_num_tx_guard + length_num_tx_guard)) goto get_num_rx_guards; if (length > offset_num_tx_guard + length_num_tx_guard) { num_tx_guard = (short)cfg_data[offset_num_tx_guard] | (short)(cfg_data[offset_num_tx_guard + 1] << 8); testing_hcd->guard_assigned += num_tx_guard; } get_num_rx_guards: /* get number of rx guards */ if (0 == (offset_num_rx_guard + length_num_rx_guard)) goto get_guards; if (length > offset_num_rx_guard + length_num_rx_guard) { num_rx_guard = (short)cfg_data[offset_num_rx_guard] | (short)(cfg_data[offset_num_rx_guard + 1] << 8); testing_hcd->guard_assigned += num_rx_guard; } get_guards: if (testing_hcd->guard_assigned > 0) LOGD(tcm_hcd->pdev->dev.parent, "num of guards = %2d\n", testing_hcd->guard_assigned); /* get tx guards mapping */ if ((num_tx_guard > 0) && (length > offset_tx_guard + length_tx_guard)) { idx = 0; for (i = 0; i < num_tx_guard; i++) { testing_hcd->guard_pins[j] = (short)cfg_data[offset_tx_guard + idx] | (short)(cfg_data[offset_tx_guard + idx + 1] << 8); LOGD(tcm_hcd->pdev->dev.parent, "guard_pins[%d] = %2d\n", i, testing_hcd->guard_pins[j]); idx += 2; j += 1; } } /* get rx guards mapping */ if ((num_rx_guard > 0) && (length > offset_rx_guard + length_rx_guard)) { idx = 0; for (i = 0; i < num_rx_guard; i++) { testing_hcd->guard_pins[j] = (short)cfg_data[offset_rx_guard + idx] | (short)(cfg_data[offset_rx_guard + idx + 1] << 8); LOGD(tcm_hcd->pdev->dev.parent, "guard_pins[%d] = %2d\n", i, testing_hcd->guard_pins[j]); idx += 2; j += 1; } } return 0; } #endif /* end of PT1_GET_PIN_ASSIGNMENT */ static int testing_pt01_trx_trx_short(void) { int retval; int i, j; int phy_pin; bool do_pin_test = false; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; unsigned int size; unsigned char limit; unsigned char *buf; unsigned char data; #ifdef PT1_GET_PIN_ASSIGNMENT unsigned char *satic_cfg_buf = NULL; unsigned int satic_cfg_length; #endif LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; #ifdef PT1_GET_PIN_ASSIGNMENT satic_cfg_length = le2_to_uint(app_info->static_config_size); if (!testing_hcd->satic_cfg_buf) { satic_cfg_buf = kzalloc(satic_cfg_length, GFP_KERNEL); if (!satic_cfg_buf) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for satic_cfg_buf\n"); goto exit; } retval = testing_get_static_config(satic_cfg_buf, satic_cfg_length); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to get static config\n"); goto exit; } testing_hcd->satic_cfg_buf = satic_cfg_buf; } if ((testing_hcd->tx_assigned <= 0) || (testing_hcd->rx_assigned <= 0) || (testing_hcd->guard_assigned <= 0)) { if (!testing_hcd->satic_cfg_buf) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to get proper satic_cfg\n"); goto exit; } retval = testing_get_pins_mapping(testing_hcd->satic_cfg_buf, satic_cfg_length); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to get pins mapping\n"); goto exit; } } #endif retval = testing_run_prod_test_item(TEST_PT1_TRX_TRX_SHORTS); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); return -EIO; } LOCK_BUFFER(testing_hcd->resp); size = sizeof(pt1_limits) / sizeof(pt1_limits[0]); if (size < testing_hcd->resp.data_length) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } buf = testing_hcd->resp.buf; testing_hcd->result = true; for (i = 0; i < testing_hcd->resp.data_length; i++) { data = buf[i]; LOGD(tcm_hcd->pdev->dev.parent, "[%d]: 0x%02x\n", i, data); for (j = 0; j < 8; j++) { phy_pin = (i*8 + j); #ifdef PT1_GET_PIN_ASSIGNMENT do_pin_test = testing_is_pins_assigned(phy_pin); #else do_pin_test = true; #endif limit = CHECK_BIT(pt1_limits[i], j); if (do_pin_test) { if (CHECK_BIT(data, j) != limit) { LOGE(tcm_hcd->pdev->dev.parent, "pin-%2d : fail\n", phy_pin); testing_hcd->result = false; } else LOGD(tcm_hcd->pdev->dev.parent, "pin-%2d : pass\n", phy_pin); } } } UNLOCK_BUFFER(testing_hcd->resp); exit: #ifdef PT1_GET_PIN_ASSIGNMENT kfree(satic_cfg_buf); #endif if (tcm_hcd->features.dual_firmware) { if (tcm_hcd->reset(tcm_hcd) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reset\n"); } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static int testing_pt05_full_raw(void) { int retval; unsigned char *buf; unsigned int idx; unsigned int row; unsigned int col; unsigned int rows; unsigned int cols; unsigned int limits_rows; unsigned int limits_cols; unsigned int frame_size; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; unsigned short data; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); frame_size = rows * cols * 2; retval = testing_run_prod_test_item(TEST_PT5_FULL_RAW_CAP); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); goto exit; } LOCK_BUFFER(testing_hcd->resp); if (frame_size != testing_hcd->resp.data_length) { LOGE(tcm_hcd->pdev->dev.parent, "Frame size mismatch\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt5_hi_limits) / sizeof(pt5_hi_limits[0]); limits_cols = sizeof(pt5_hi_limits[0]) / sizeof(pt5_hi_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt5_lo_limits) / sizeof(pt5_lo_limits[0]); limits_cols = sizeof(pt5_lo_limits[0]) / sizeof(pt5_lo_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } buf = testing_hcd->resp.buf; testing_hcd->result = true; idx = 0; for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { data = (unsigned short)(buf[idx] & 0xff) | (unsigned short)(buf[idx+1] << 8); if (data > pt5_hi_limits[row][col] || data < pt5_lo_limits[row][col]) { LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = (%4d, %4d)\n", row, col, data, pt5_lo_limits[row][col], pt5_hi_limits[row][col]); testing_hcd->result = false; } idx += 2; } } UNLOCK_BUFFER(testing_hcd->resp); retval = 0; exit: if (tcm_hcd->features.dual_firmware) { if (tcm_hcd->reset(tcm_hcd) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reset\n"); } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static int testing_pt07_dynamic_range(void) { int retval; unsigned char *buf; unsigned int idx; unsigned int row; unsigned int col; unsigned int data; unsigned int rows; unsigned int cols; unsigned int limits_rows; unsigned int limits_cols; unsigned int frame_size_words; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); testing_get_frame_size_words(&frame_size_words, false); retval = testing_run_prod_test_item(TEST_PT7_DYNAMIC_RANGE); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); goto exit; } LOCK_BUFFER(testing_hcd->resp); if (frame_size_words != testing_hcd->resp.data_length / 2) { LOGE(tcm_hcd->pdev->dev.parent, "Frame size mismatch\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt7_hi_limits) / sizeof(pt7_hi_limits[0]); limits_cols = sizeof(pt7_hi_limits[0]) / sizeof(pt7_hi_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt7_lo_limits) / sizeof(pt7_lo_limits[0]); limits_cols = sizeof(pt7_lo_limits[0]) / sizeof(pt7_lo_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } idx = 0; buf = testing_hcd->resp.buf; testing_hcd->result = true; for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { data = le2_to_uint(&buf[idx * 2]); if (data > pt7_hi_limits[row][col] || data < pt7_lo_limits[row][col]) { LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = (%4d, %4d)\n", row, col, data, pt7_lo_limits[row][col], pt7_hi_limits[row][col]); testing_hcd->result = false; } idx++; } } UNLOCK_BUFFER(testing_hcd->resp); testing_standard_frame_output(false); retval = 0; exit: if (tcm_hcd->features.dual_firmware) { if (tcm_hcd->reset(tcm_hcd) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reset\n"); } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static int testing_pt10_noise(void) { int retval; short data; unsigned char *buf; unsigned int idx; unsigned int row; unsigned int col; unsigned int rows; unsigned int cols; unsigned int limits_rows; unsigned int limits_cols; unsigned int frame_size_words; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); testing_get_frame_size_words(&frame_size_words, true); retval = testing_run_prod_test_item(TEST_PT10_DELTA_NOISE); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); goto exit; } LOCK_BUFFER(testing_hcd->resp); if (frame_size_words != testing_hcd->resp.data_length / 2) { LOGE(tcm_hcd->pdev->dev.parent, "Frame size mismatch\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt10_limits) / sizeof(pt10_limits[0]); limits_cols = sizeof(pt10_limits[0]) / sizeof(pt10_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } idx = 0; buf = testing_hcd->resp.buf; testing_hcd->result = true; for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { data = (short)le2_to_uint(&buf[idx * 2]); if (data > pt10_limits[row][col]) { LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = %4d\n", row, col, data, pt10_limits[row][col]); testing_hcd->result = false; } idx++; } } UNLOCK_BUFFER(testing_hcd->resp); testing_standard_frame_output(false); retval = 0; exit: if (tcm_hcd->features.dual_firmware) { if (tcm_hcd->reset(tcm_hcd) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reset\n"); } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static int testing_do_test_item(enum test_code test_item, int limit_rows, int limit_cols, const int* limit_data_low, const int *limit_data_high, struct file *fp, char *output_str, int str_size) { int retval; int data, max_data, min_data, ave_data, sum_data; unsigned char *buf; int idx; unsigned int row; unsigned int col; unsigned int rows; unsigned int cols; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); max_data = 0; min_data = 0; ave_data = 0; sum_data = 0; if (fp) { ovt_tcm_store_to_file(fp, "\n%s do test item %d enter\n", __func__, test_item); } if (output_str) { snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "%s do test item %d enter\n", __func__, test_item); } retval = testing_run_prod_test_item(test_item); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "%s fail to get cmd response of %d\n", __func__, test_item); goto exit; } LOCK_BUFFER(testing_hcd->resp); idx = 0; buf = testing_hcd->resp.buf; testing_hcd->result = true; if ((limit_cols != cols) || (limit_rows != rows)) { LOGE(tcm_hcd->pdev->dev.parent, "incorrect cols and rows size of item:%d\n", test_item); snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "%s incorrect cols and rows size of item:%d\n", __func__, test_item); testing_hcd->result = false; goto exit; } for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { int limit_low, limit_high; if ((limit_cols == 1) && (limit_rows == 1)) { if (limit_data_low) limit_low = limit_data_low[0]; if (limit_data_high) limit_high = limit_data_high[0]; } else { if (limit_data_low) limit_low = limit_data_low[row * limit_cols + col]; if (limit_data_high) limit_high = limit_data_high[row * limit_cols + col]; } if (test_item == TEST_PT10_DELTA_NOISE || test_item == TEST_PT11_OPEN_DETECTION) { data = (short)le2_to_uint(&buf[idx * 2]); } else { data = (unsigned short)le2_to_uint(&buf[idx * 2]); } if (idx == 0) { min_data = data; max_data = data; } if (fp) { ovt_tcm_store_to_file(fp, "%d,", data); } if (output_str) { snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "%d,", data); } if ((idx + 1) % cols == 0 && idx != 0) { if (fp) ovt_tcm_store_to_file(fp, "\n"); if (output_str) { snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "\n"); } } sum_data += data; if (data > max_data) { max_data = data; } if (data < min_data) { min_data = data; } if (limit_data_low) { if (data < limit_low) { testing_hcd->result = false; LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = %4d\n", row, col, data, limit_data_low[row * limit_cols + col]); } } if (limit_data_high) { if (data > limit_high) { testing_hcd->result = false; LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = %4d\n", row, col, data, limit_data_high[row * limit_cols + col]); } } idx++; } } if (idx != 0) ave_data = sum_data / idx; UNLOCK_BUFFER(testing_hcd->resp); retval = 0; exit: if (fp) { ovt_tcm_store_to_file(fp, "\n%s do test item min:%d max:%d ave:%d\n", __func__, min_data, max_data, ave_data); ovt_tcm_store_to_file(fp, "\n%s do test item %d end, result is %s\n", __func__, test_item, (testing_hcd->result)?"pass":"fail"); } if (output_str) { snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "\n%s do test item min:%d max:%d ave:%d\n", __func__, min_data, max_data, ave_data); snprintf(output_str + strlen(output_str), str_size - strlen(output_str), "\n%s do test item %d end, result is %s\n", __func__, test_item, (testing_hcd->result)?"pass":"fail"); } LOGN(tcm_hcd->pdev->dev.parent, "test item:%d Result = %s\n", test_item, (testing_hcd->result)?"pass":"fail"); return (testing_hcd->result)? 0 : -1; } static char *get_date_time_str(void) { static char time_data_buf[128] = { 0 }; #if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) struct timespec now_time; struct rtc_time rtc_now_time; getnstimeofday(&now_time); rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, rtc_now_time.tm_sec); #else struct tm tm; time64_to_tm(ktime_get_real_seconds(), 0, &tm); snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", (int)(tm.tm_year + 1900), tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); #endif return time_data_buf; } static int testing_do_testing(void) { int retval; int error_count = 0; unsigned int rows; unsigned int cols; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; #ifdef LIMIT_FROM_CSV_FILE char file_path[256]; struct file *fp = NULL; loff_t pos; #if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) mm_segment_t old_fs; #endif #endif app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); if (!g_testing_output_buf) { g_testing_output_buf = vmalloc(OUTPUT_TO_CSV_STRING_LEN); if (!g_testing_output_buf) { LOGE(tcm_hcd->pdev->dev.parent, "can not alloc buffer for g_testing_output_buf\n"); return -1; } } memset(g_testing_output_buf, 0, OUTPUT_TO_CSV_STRING_LEN); snprintf(g_testing_output_buf + strlen(g_testing_output_buf), OUTPUT_TO_CSV_STRING_LEN - strlen(g_testing_output_buf), "start to do test,\n"); #ifdef LIMIT_FROM_CSV_FILE retval = ovt_tcm_get_thr_from_csvfile(); if (retval < 0) { printk("ovt_tcm_get_thr_from_csvfile error, return\n"); snprintf(g_testing_output_buf + strlen(g_testing_output_buf), OUTPUT_TO_CSV_STRING_LEN - strlen(g_testing_output_buf), "ovt_tcm_get_thr_from_csvfile error\n"); error_count++; goto sys_err; } retval = testing_do_test_item(TEST_PT7_DYNAMIC_RANGE, TX_NUM_MAX, RX_NUM_MAX, testing_hcd->testing_csv_threshold.raw_data_min_limits, testing_hcd->testing_csv_threshold.raw_data_max_limits, fp, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT10_DELTA_NOISE, TX_NUM_MAX, RX_NUM_MAX, NULL, testing_hcd->testing_csv_threshold.lcd_noise_max_limits, fp, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT11_OPEN_DETECTION, TX_NUM_MAX, RX_NUM_MAX, testing_hcd->testing_csv_threshold.open_short_min_limits, testing_hcd->testing_csv_threshold.open_short_max_limits, fp, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT17_ADC_RANGE, TX_NUM_MAX, RX_NUM_MAX, NULL, testing_hcd->testing_csv_threshold.pt17_max_limits, fp, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT18_HYBRID_ABS_RAW, TX_NUM_MAX, RX_NUM_MAX, testing_hcd->testing_csv_threshold.pt18_min_limits, testing_hcd->testing_csv_threshold.pt18_max_limits, fp, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } #else retval = testing_do_test_item(TEST_PT7_DYNAMIC_RANGE, TEST_LIMIT_ROW_CNT, TEST_LIMIT_COL_CNT, pt7_low_limits_new, pt7_hi_limits_new, NULL, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT10_DELTA_NOISE, TEST_LIMIT_ROW_CNT, TEST_LIMIT_COL_CNT, NULL, pt10_high_limits_new, NULL, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT11_OPEN_DETECTION, TEST_LIMIT_ROW_CNT, TEST_LIMIT_COL_CNT, pt11_low_limits_new, pt11_hi_limits_new, NULL, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT17_ADC_RANGE, TEST_LIMIT_ROW_CNT, TEST_LIMIT_COL_CNT, NULL, pt17_hi_limits_new, NULL, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } retval = testing_do_test_item(TEST_PT18_HYBRID_ABS_RAW, TEST_LIMIT_ROW_CNT, TEST_LIMIT_COL_CNT, pt18_low_limits_new, pt18_hi_limits_new, NULL, g_testing_output_buf, OUTPUT_TO_CSV_STRING_LEN); if (retval < 0) { error_count++; } #endif #ifdef LIMIT_FROM_CSV_FILE if (error_count) { //test fail result sprintf(file_path, "/data/tp_%s_test_data_%s-fail.csv", tcm_hcd->id_info.part_number,get_date_time_str()); } else { //test pass result sprintf(file_path, "/data/tp_%s_test_data_%s-success.csv", tcm_hcd->id_info.part_number,get_date_time_str()); } fp = filp_open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 644); if (IS_ERR_OR_NULL(fp)) { printk("ovt tcm Open log file '%s' failed.\n", file_path); snprintf(g_testing_output_buf + strlen(g_testing_output_buf), OUTPUT_TO_CSV_STRING_LEN - strlen(g_testing_output_buf), "can not open file:%s\n", file_path); goto sys_err; } pos = 0; #if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) old_fs = get_fs(); set_fs(KERNEL_DS); vfs_write(fp, g_testing_output_buf, strlen(g_testing_output_buf), &pos); printk("g_testing_output_buf , strlen is %d\n", (int)strlen(g_testing_output_buf)); if (!IS_ERR_OR_NULL(fp)) { printk("ovt tcm csv parser: filp close\n"); filp_close(fp, NULL); fp = NULL; } set_fs(old_fs); #else kernel_write(fp, g_testing_output_buf, strlen(g_testing_output_buf), &pos); filp_close(fp, NULL); #endif #endif #ifdef LIMIT_FROM_CSV_FILE sys_err: #endif if (error_count) { return -1; } return 0; } static int testing_pt11_open_detection(void) { int retval; short data; unsigned char *buf; unsigned int idx; unsigned int row; unsigned int col; unsigned int rows; unsigned int cols; unsigned int limits_rows; unsigned int limits_cols; unsigned int image_size_words; struct ovt_tcm_app_info *app_info; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; app_info = &tcm_hcd->app_info; rows = le2_to_uint(app_info->num_of_image_rows); cols = le2_to_uint(app_info->num_of_image_cols); testing_get_frame_size_words(&image_size_words, true); retval = testing_run_prod_test_item(TEST_PT11_OPEN_DETECTION); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run test\n"); goto exit; } LOCK_BUFFER(testing_hcd->resp); if (image_size_words != testing_hcd->resp.data_length / 2) { LOGE(tcm_hcd->pdev->dev.parent, "Image size mismatch\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt11_hi_limits) / sizeof(pt11_hi_limits[0]); limits_cols = sizeof(pt11_hi_limits[0]) / sizeof(pt11_hi_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } limits_rows = sizeof(pt11_lo_limits) / sizeof(pt11_lo_limits[0]); limits_cols = sizeof(pt11_lo_limits[0]) / sizeof(pt11_lo_limits[0][0]); if (rows > limits_rows || cols > limits_cols) { LOGE(tcm_hcd->pdev->dev.parent, "Mismatching limits data\n"); UNLOCK_BUFFER(testing_hcd->resp); retval = -EINVAL; goto exit; } idx = 0; buf = testing_hcd->resp.buf; testing_hcd->result = true; for (row = 0; row < rows; row++) { for (col = 0; col < cols; col++) { data = (short)le2_to_uint(&buf[idx * 2]); if (data > pt11_hi_limits[row][col] || data < pt11_lo_limits[row][col]) { LOGE(tcm_hcd->pdev->dev.parent, "fail at (%2d, %2d) data = %5d, limit = (%4d, %4d)\n", row, col, data, pt11_lo_limits[row][col], pt11_hi_limits[row][col]); testing_hcd->result = false; } idx++; } } UNLOCK_BUFFER(testing_hcd->resp); testing_standard_frame_output(true); retval = 0; exit: if (tcm_hcd->features.dual_firmware) { if (tcm_hcd->reset(tcm_hcd) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reset\n"); } } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static int testing_raw_data(void) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; retval = testing_collect_reports(REPORT_RAW,1); if (retval >= 0) { LOGN(tcm_hcd->pdev->dev.parent, "success to collect raw data\n"); } else { LOGN(tcm_hcd->pdev->dev.parent, "fail to collect raw data\n"); } return retval; } static int testing_delta_data(void) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; retval = testing_collect_reports(REPORT_DELTA,1); if (retval >= 0) { LOGN(tcm_hcd->pdev->dev.parent, "success to collect raw data\n"); } else { LOGN(tcm_hcd->pdev->dev.parent, "fail to collect raw data\n"); } return retval; } static int testing_reset_open(void) { int retval; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; const struct ovt_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; LOGN(tcm_hcd->pdev->dev.parent, "Start testing\n"); testing_hcd->result = false; if (bdata->reset_gpio < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Hardware reset unavailable\n"); return -EINVAL; } mutex_lock(&tcm_hcd->reset_mutex); #ifdef WATCHDOG_SW tcm_hcd->update_watchdog(tcm_hcd, false); #endif gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); msleep(bdata->reset_active_ms); gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); msleep(bdata->reset_delay_ms); #ifdef WATCHDOG_SW tcm_hcd->update_watchdog(tcm_hcd, true); #endif mutex_unlock(&tcm_hcd->reset_mutex); if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) { retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to enter bootloader mode\n"); return retval; } } else { retval = tcm_hcd->identify(tcm_hcd, false); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to do identification\n"); goto run_app_firmware; } } if (tcm_hcd->boot_info.last_reset_reason == reset_open_limit) testing_hcd->result = true; else testing_hcd->result = false; retval = 0; run_app_firmware: if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to run application firmware\n"); } LOGN(tcm_hcd->pdev->dev.parent, "Result = %s\n", (testing_hcd->result)?"pass":"fail"); return retval; } static void testing_report(void) { int retval; unsigned int offset; unsigned int report_size; struct ovt_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; report_size = tcm_hcd->report.buffer.data_length; LOCK_BUFFER(testing_hcd->report); if (testing_hcd->report_index == 0) { retval = ovt_tcm_alloc_mem(tcm_hcd, &testing_hcd->report, report_size * testing_hcd->num_of_reports); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for testing_hcd->report.buf\n"); UNLOCK_BUFFER(testing_hcd->report); return; } } if (testing_hcd->report_index < testing_hcd->num_of_reports) { offset = report_size * testing_hcd->report_index; retval = secure_memcpy(testing_hcd->report.buf + offset, testing_hcd->report.buf_size - offset, tcm_hcd->report.buffer.buf, tcm_hcd->report.buffer.buf_size, tcm_hcd->report.buffer.data_length); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to copy report data\n"); UNLOCK_BUFFER(testing_hcd->report); return; } testing_hcd->report_index++; testing_hcd->report.data_length += report_size; } UNLOCK_BUFFER(testing_hcd->report); if (testing_hcd->report_index == testing_hcd->num_of_reports) complete(&report_complete); return; } static int testing_init(struct ovt_tcm_hcd *tcm_hcd) { int retval; int idx; testing_hcd = kzalloc(sizeof(*testing_hcd), GFP_KERNEL); if (!testing_hcd) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to allocate memory for testing_hcd\n"); return -ENOMEM; } testing_hcd->tcm_hcd = tcm_hcd; testing_hcd->collect_reports = testing_collect_reports; INIT_BUFFER(testing_hcd->out, false); INIT_BUFFER(testing_hcd->resp, false); INIT_BUFFER(testing_hcd->report, false); INIT_BUFFER(testing_hcd->process, false); INIT_BUFFER(testing_hcd->output, false); testing_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, tcm_hcd->sysfs_dir); if (!testing_hcd->sysfs_dir) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to create sysfs directory\n"); retval = -EINVAL; goto err_sysfs_create_dir; } for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { //retval = sysfs_create_file(testing_hcd->sysfs_dir, retval = sysfs_create_file(&tcm_hcd->pdev->dev.kobj, //default path &(*attrs[idx]).attr); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to create sysfs file\n"); goto err_sysfs_create_file; } } retval = sysfs_create_bin_file(testing_hcd->sysfs_dir, &bin_attr); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, "Failed to create sysfs bin file\n"); goto err_sysfs_create_bin_file; } testing_hcd->proc_csv = proc_create_data("ovt_test_csv", 0777, NULL, &ovt_proccsv_fops, NULL); if (NULL == testing_hcd->proc_csv) { LOGE(tcm_hcd->pdev->dev.parent, "create proc_csv entry fail"); } return 0; err_sysfs_create_bin_file: err_sysfs_create_file: for (idx--; idx >= 0; idx--) sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); kobject_put(testing_hcd->sysfs_dir); err_sysfs_create_dir: RELEASE_BUFFER(testing_hcd->output); RELEASE_BUFFER(testing_hcd->process); RELEASE_BUFFER(testing_hcd->report); RELEASE_BUFFER(testing_hcd->resp); RELEASE_BUFFER(testing_hcd->out); kfree(testing_hcd); testing_hcd = NULL; return retval; } static int testing_remove(struct ovt_tcm_hcd *tcm_hcd) { int idx; if (!testing_hcd) goto exit; sysfs_remove_bin_file(testing_hcd->sysfs_dir, &bin_attr); for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); kobject_put(testing_hcd->sysfs_dir); RELEASE_BUFFER(testing_hcd->output); RELEASE_BUFFER(testing_hcd->process); RELEASE_BUFFER(testing_hcd->report); RELEASE_BUFFER(testing_hcd->resp); RELEASE_BUFFER(testing_hcd->out); if (testing_hcd->proc_csv) { proc_remove(testing_hcd->proc_csv); } kfree(testing_hcd); testing_hcd = NULL; exit: complete(&testing_remove_complete); return 0; } static int testing_reinit(struct ovt_tcm_hcd *tcm_hcd) { int retval; if (!testing_hcd) { retval = testing_init(tcm_hcd); return retval; } return 0; } static int testing_syncbox(struct ovt_tcm_hcd *tcm_hcd) { if (!testing_hcd) return 0; if (tcm_hcd->report.id == testing_hcd->report_type) testing_report(); return 0; } static struct ovt_tcm_module_cb testing_module = { .type = TCM_TESTING, .init = testing_init, .remove = testing_remove, .syncbox = testing_syncbox, #ifdef REPORT_NOTIFIER .asyncbox = NULL, #endif .reinit = testing_reinit, .suspend = NULL, .resume = NULL, .early_suspend = NULL, }; #ifdef BUILD_AS_KO_MODULE int testing_module_init(void) { return ovt_tcm_add_module(&testing_module, true); } void testing_module_exit(void) { ovt_tcm_add_module(&testing_module, false); wait_for_completion(&testing_remove_complete); return; } EXPORT_SYMBOL(testing_module_init); EXPORT_SYMBOL(testing_module_exit); #else static int __init testing_module_init(void) { return ovt_tcm_add_module(&testing_module, true); } static void __exit testing_module_exit(void) { ovt_tcm_add_module(&testing_module, false); wait_for_completion(&testing_remove_complete); return; } module_init(testing_module_init); module_exit(testing_module_exit); MODULE_AUTHOR("Omnivision, Inc."); MODULE_DESCRIPTION("Omnivision TCM Testing Module"); MODULE_LICENSE("GPL v2"); #endif