blob: e8c9ac28eb347c1b0e2216d922e02ac66b8b6f19 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Synaptics TouchCom touchscreen driver
*
* Copyright (C) 2017-2020 Synaptics 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 SYNAPTICS
* 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 SYNAPTICS 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 SYNAPTICS 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, SYNAPTICS'
* TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
* DOLLARS.
*/
/**
* @file syna_tcm2_testing.c
*
* This file implements the sample code to perform chip testing.
*/
#include "syna_tcm2_testing.h"
#include "syna_tcm2_testing_limits.h"
#include "synaptics_touchcom_core_dev.h"
#include "synaptics_touchcom_func_base.h"
/* g_testing_dir represents the root folder of testing sysfs
*/
static struct kobject *g_testing_dir;
static struct syna_tcm *g_tcm_ptr;
/**
* syna_testing_compare_byte_vector()
*
* Sample code to compare the test result with limits
* by byte vector
*
* @param
* [ in] data: target test data
* [ in] data_size: size of test data
* [ in] limit: test limit value to be compared with
* [ in] limit_size: size of test limit
*
* @return
* on success, true; otherwise, return false
*/
static bool syna_testing_compare_byte_vector(unsigned char *data,
unsigned int data_size, const unsigned char *limit,
unsigned int limit_size)
{
bool result = false;
unsigned char tmp;
unsigned char p, l;
int i, j;
if (!data || (data_size == 0)) {
LOGE("Invalid test data\n");
return false;
}
if (!limit || (limit_size == 0)) {
LOGE("Invalid limits\n");
return false;
}
if (limit_size < data_size) {
LOGE("Limit size mismatched, data size: %d, limits: %d\n",
data_size, limit_size);
return false;
}
result = true;
for (i = 0; i < data_size; i++) {
tmp = data[i];
for (j = 0; j < 8; j++) {
p = GET_BIT(tmp, j);
l = GET_BIT(limit[i], j);
if (p != l) {
LOGE("Fail on TRX-%03d (data:%X, limit:%X)\n",
(i*8 + j), p, l);
result = false;
}
}
}
return result;
}
/**
* syna_testing_compare_frame()
*
* Sample code to compare the test result with limits
* being formatted as a frame
*
* @param
* [ in] data: target test data
* [ in] data_size: size of test data
* [ in] rows: the number of rows
* [ in] cols: the number of column
* [ in] limits_hi: upper-bound test limit
* [ in] limits_lo: lower-bound test limit
*
* @return
* on success, true; otherwise, return false
*/
static bool syna_testing_compare_frame(unsigned char *data,
unsigned int data_size, int rows, int cols,
const short *limits_hi, const short *limits_lo)
{
bool result = false;
short *data_ptr = NULL;
short limit;
int i, j;
if (!data || (data_size == 0)) {
LOGE("Invalid test data\n");
return false;
}
if (data_size < (2 * rows * cols)) {
LOGE("Size mismatched, data:%d (exppected:%d)\n",
data_size, (2 * rows * cols));
result = false;
return false;
}
if (rows > LIMIT_BOUNDARY) {
LOGE("Rows mismatched, rows:%d (exppected:%d)\n",
rows, LIMIT_BOUNDARY);
result = false;
return false;
}
if (cols > LIMIT_BOUNDARY) {
LOGE("Columns mismatched, cols: %d (exppected:%d)\n",
cols, LIMIT_BOUNDARY);
result = false;
return false;
}
result = true;
if (!limits_hi)
goto end_of_upper_bound_limit;
data_ptr = (short *)&data[0];
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
limit = limits_hi[i * LIMIT_BOUNDARY + j];
if (*data_ptr > limit) {
LOGE("Fail on (%2d,%2d)=%5d, limits_hi:%4d\n",
i, j, *data_ptr, limit);
result = false;
}
data_ptr++;
}
}
end_of_upper_bound_limit:
if (!limits_lo)
goto end_of_lower_bound_limit;
data_ptr = (short *)&data[0];
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
limit = limits_lo[i * LIMIT_BOUNDARY + j];
if (*data_ptr < limit) {
LOGE("Fail on (%2d,%2d)=%5d, limits_lo:%4d\n",
i, j, *data_ptr, limit);
result = false;
}
data_ptr++;
}
}
end_of_lower_bound_limit:
return result;
}
/**
* syna_testing_compare_list()
*
* Sample code to compare the test result with limits
* being formatted as a list
*
* @param
* [ in] data: target test data
* [ in] data_size: size of test data
* [ in] rows: the number of rows
* [ in] cols: the number of column
* [ in] limits_hi: upper-bound test limit
* [ in] limits_lo: lower-bound test limit
*
* @return
* on success, true; otherwise, return false
*/
static bool syna_testing_compare_list(unsigned char *data,
unsigned int data_size, int rows, int cols,
const int *limits_hi, const int *limits_lo)
{
bool result = false;
int *data_ptr = NULL;
int limit;
int i;
if (!data || (data_size == 0)) {
LOGE("Invalid test data\n");
return false;
}
if (data_size % (rows + cols) != 0) {
LOGE("Size mismatched, data:%d (exppected:%d * N)\n",
data_size, (rows + cols));
result = false;
return false;
}
if (rows > LIMIT_BOUNDARY) {
LOGE("Rows mismatched, rows:%d (exppected:%d)\n",
rows, LIMIT_BOUNDARY);
result = false;
return false;
}
if (cols > LIMIT_BOUNDARY) {
LOGE("Columns mismatched, cols: %d (exppected:%d)\n",
cols, LIMIT_BOUNDARY);
result = false;
return false;
}
result = true;
if (!limits_hi)
goto end_of_upper_bound_limit;
data_ptr = (int *)&data[0];
for (i = 0; i < cols; i++) {
limit = limits_hi[i];
if (*data_ptr > limit) {
LOGE("Fail on cols-%2d=%5d, limits_hi:%4d\n",
i, *data_ptr, limit);
result = false;
}
data_ptr++;
}
for (i = 0; i < rows; i++) {
limit = limits_hi[LIMIT_BOUNDARY + i];
if (*data_ptr > limit) {
LOGE("Fail on row-%2d=%5d, limits_hi:%4d\n",
i, *data_ptr, limit);
result = false;
}
data_ptr++;
}
end_of_upper_bound_limit:
if (!limits_lo)
goto end_of_lower_bound_limit;
data_ptr = (int *)&data[0];
for (i = 0; i < cols; i++) {
limit = limits_lo[i];
if (*data_ptr < limit) {
LOGE("Fail on cols-%2d=%5d, limits_lo:%4d\n",
i, *data_ptr, limit);
result = false;
}
data_ptr++;
}
for (i = 0; i < rows; i++) {
limit = limits_lo[LIMIT_BOUNDARY + i];
if (*data_ptr < limit) {
LOGE("Fail on row-%2d=%5d, limits_lo:%4d\n",
i, *data_ptr, limit);
result = false;
}
data_ptr++;
}
end_of_lower_bound_limit:
return result;
}
/**
* syna_testing_device_id()
*
* Sample code to ensure the device id is expected
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_device_id(struct syna_tcm *tcm)
{
int retval;
bool result;
struct tcm_identification_info info;
char *strptr = NULL;
LOGI("Start testing\n");
retval = syna_tcm_identify(tcm->tcm_dev, &info);
if (retval < 0) {
LOGE("Fail to get identification\n");
result = false;
goto exit;
}
strptr = strnstr(info.part_number,
device_id_limit,
strlen(info.part_number));
if (strptr != NULL)
result = true;
else {
LOGE("Device ID mismatched, FW: %s (limit: %s)\n",
info.part_number, device_id_limit);
result = false;
}
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_config_id()
*
* Sample code to ensure the config id is expected
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_config_id(struct syna_tcm *tcm)
{
int retval;
bool result;
struct tcm_application_info info;
int idx;
LOGI("Start testing\n");
retval = syna_tcm_get_app_info(tcm->tcm_dev, &info);
if (retval < 0) {
LOGE("Fail to get app info\n");
result = false;
goto exit;
}
result = true;
for (idx = 0; idx < sizeof(config_id_limit); idx++) {
if (config_id_limit[idx] != info.customer_config_id[idx]) {
LOGE("Fail on byte.%d (data: %02X, limit: %02X)\n",
idx, info.customer_config_id[idx],
config_id_limit[idx]);
result = false;
}
}
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_check_id_show()
*
* Attribute to show the result of ID comparsion to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_check_id_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
if (!tcm->is_connected) {
retval = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
count = 0;
retval = syna_testing_device_id(tcm);
retval = scnprintf(buf, PAGE_SIZE - count,
"Device ID check: %s\n",
(retval < 0) ? "fail" : "pass");
buf += retval;
count += retval;
retval = syna_testing_config_id(tcm);
retval = scnprintf(buf, PAGE_SIZE - count,
"Config ID check: %s\n",
(retval < 0) ? "fail" : "pass");
buf += retval;
count += retval;
retval = count;
exit:
return retval;
}
static struct kobj_attribute kobj_attr_check_id =
__ATTR(check_id, 0444, syna_testing_check_id_show, NULL);
/**
* syna_testing_pt01()
*
* Sample code to perform PT01 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt01(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID01_TRX_TRX_SHORTS,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID01_TRX_TRX_SHORTS);
result = false;
goto exit;
}
result = syna_testing_compare_byte_vector(test_data->buf,
test_data->data_length,
pt01_limits,
ARRAY_SIZE(pt01_limits));
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt01_show()
*
* Attribute to show the result of PT01 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt01_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt01(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST PT$01: %s\n", (retval < 0) ? "fail" : "pass");
for (i = 0; i < test_data.data_length; i++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
test_data.buf[i]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt01 =
__ATTR(pt01, 0444, syna_testing_pt01_show, NULL);
/**
* syna_testing_pt05()
*
* Sample code to perform PT05 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt05(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID05_FULL_RAW_CAP,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID05_FULL_RAW_CAP);
result = false;
goto exit;
}
result = syna_testing_compare_frame(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const short *)&pt05_hi_limits[0],
(const short *)&pt05_lo_limits[0]);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt05_show()
*
* Attribute to show the result of PT05 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt05_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt05(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST PT$05: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt05 =
__ATTR(pt05, 0444, syna_testing_pt05_show, NULL);
/**
* syna_testing_pt0a()
*
* Sample code to perform PT0A testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt0a(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID10_DELTA_NOISE,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID10_DELTA_NOISE);
result = false;
goto exit;
}
result = syna_testing_compare_frame(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const short *)&pt0a_hi_limits[0],
(const short *)&pt0a_lo_limits[0]);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt0a_show()
*
* Attribute to show the result of PT0A test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt0a_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt0a(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST PT$0A: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt0a =
__ATTR(pt0a, 0444, syna_testing_pt0a_show, NULL);
/**
* syna_testing_pt10()
*
* Sample code to perform PT10 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt10(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID16_SENSOR_SPEED,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID16_SENSOR_SPEED);
result = false;
goto exit;
}
result = syna_testing_compare_frame(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const short *)&pt10_hi_limits[0],
(const short *)&pt10_lo_limits[0]);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt10_show()
*
* Attribute to show the result of PT10 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt10_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt10(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST PT$10: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt10 =
__ATTR(pt10, 0444, syna_testing_pt10_show, NULL);
/**
* syna_testing_pt11()
*
* Sample code to perform PT11 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt11(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID17_ADC_RANGE,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID17_ADC_RANGE);
result = false;
goto exit;
}
result = syna_testing_compare_frame(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const short *)&pt11_hi_limits[0],
(const short *)&pt11_lo_limits[0]);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt11_show()
*
* Attribute to show the result of PT11 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt11_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt11(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST PT$11: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt11 =
__ATTR(pt11, 0444, syna_testing_pt11_show, NULL);
/**
* syna_testing_pt12()
*
* Sample code to perform PT12 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt12(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID18_HYBRID_ABS_RAW,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID18_HYBRID_ABS_RAW);
result = false;
goto exit;
}
result = syna_testing_compare_list(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const int *)&pt12_limits[0],
NULL);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt12_show()
*
* Attribute to show the result of PT12 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt12_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
int *data_ptr = NULL;
if (!tcm->is_connected) {
count = snprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt12(tcm, &test_data);
count = snprintf(buf, PAGE_SIZE,
"TEST PT$12: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (int *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->cols; i++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
for (i = 0; i < tcm->tcm_dev->rows; i++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[tcm->tcm_dev->cols + i]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt12 =
__ATTR(pt12, 0444, syna_testing_pt12_show, NULL);
/**
* syna_testing_pt16()
*
* Sample code to perform PT16 testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt16(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
LOGI("Start testing\n");
retval = syna_tcm_run_production_test(tcm->tcm_dev,
TEST_PID22_TRANS_CAP_RAW,
test_data);
if (retval < 0) {
LOGE("Fail to run test %d\n", TEST_PID22_TRANS_CAP_RAW);
result = false;
goto exit;
}
result = syna_testing_compare_frame(test_data->buf,
test_data->data_length,
tcm->tcm_dev->rows,
tcm->tcm_dev->cols,
(const short *)&pt16_hi_limits[0],
(const short *)&pt16_lo_limits[0]);
exit:
LOGI("Result = %s\n", (result)?"pass":"fail");
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt16_show()
*
* Attribute to show the result of PT11 test to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt16_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = snprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt16(tcm, &test_data);
count = snprintf(buf, PAGE_SIZE,
"TEST PT$16: %s\n", (retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt16 =
__ATTR(pt16, 0444, syna_testing_pt16_show, NULL);
/**
* syna_testing_pt_tag_moisture()
*
* Sample code to perform Tags Moisture (RID30) testing
*
* @param
* [ in] tcm: the driver handle
*
* @return
* on success, 0; otherwise, negative value on error.
*/
static int syna_testing_pt_tag_moisture(struct syna_tcm *tcm, struct tcm_buffer *test_data)
{
int retval;
bool result = false;
unsigned char code;
int attempt = 0;
short *data_ptr = NULL;
short limit;
int i, j, rows, cols;
rows = tcm->tcm_dev->rows;
cols = tcm->tcm_dev->cols;
LOGI("Start testing\n");
/* do test in polling; disable irq */
if (tcm->hw_if->ops_enable_irq)
tcm->hw_if->ops_enable_irq(tcm->hw_if, false);
syna_tcm_set_dynamic_config(tcm->tcm_dev, DC_DISABLE_DOZE, 1, RESP_IN_POLLING);
retval = syna_tcm_enable_report(tcm->tcm_dev, 30, true);
if (retval < 0) {
LOGE("Fail to enable RID30\n");
result = false;
goto exit;
}
for (attempt = 0; attempt < 3; attempt++) {
retval = syna_tcm_get_event_data(tcm->tcm_dev, &code,
test_data);
if ((retval >= 0) && (code == 30))
break;
}
if ((retval < 0) || (code != 30)) {
LOGE("Fail to get a frame of RID30 to test\n");
result = false;
goto exit;
}
if (test_data->data_length % (rows * cols) != 0) {
LOGE("Invalid frame size of RID30, %d\n", test_data->data_length);
result = false;
goto exit;
}
retval = syna_tcm_enable_report(tcm->tcm_dev, 30, false);
if (retval < 0) {
LOGE("Fail to disable RID30\n");
result = false;
goto exit;
}
/* compare to the limits */
result = true;
data_ptr = (short *)&test_data->buf[0];
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
if (*data_ptr < 0)
continue;
limit = pt_moisture_limits[i * LIMIT_BOUNDARY + j];
if (*data_ptr > limit) {
LOGE("Fail on (%2d,%2d)=%5d, limits_hi:%4d\n",
i, j, *data_ptr, limit);
result = false;
}
data_ptr++;
}
}
exit:
syna_tcm_set_dynamic_config(tcm->tcm_dev, DC_DISABLE_DOZE, 0, RESP_IN_POLLING);
LOGI("Result = %s\n", (result)?"pass":"fail");
/* recover the irq */
if (tcm->hw_if->ops_enable_irq)
tcm->hw_if->ops_enable_irq(tcm->hw_if, true);
return ((result) ? 0 : -1);
}
/**
* syna_testing_pt_moisture_show()
*
* Attribute to show the result of Tags Moisture (RID30) to the console.
*
* @param
* [ in] kobj: an instance of kobj
* [ in] attr: an instance of kobj attribute structure
* [out] buf: string buffer shown on console
*
* @return
* on success, number of characters being output;
* otherwise, negative value on error.
*/
static ssize_t syna_testing_pt_moisture_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int retval, i, j;
short *data_ptr = NULL;
unsigned int count = 0;
struct syna_tcm *tcm = g_tcm_ptr;
struct tcm_buffer test_data;
if (!tcm->is_connected) {
count = scnprintf(buf, PAGE_SIZE,
"Device is NOT connected\n");
goto exit;
}
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, true);
syna_tcm_buf_init(&test_data);
retval = syna_testing_pt_tag_moisture(tcm, &test_data);
count += scnprintf(buf, PAGE_SIZE,
"TEST Tags Moisture: %s\n",
(retval < 0) ? "fail" : "pass");
count += scnprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
tcm->tcm_dev->cols, tcm->tcm_dev->rows);
if (retval == 0) {
data_ptr = (short *)&(test_data.buf[0]);
for (i = 0; i < tcm->tcm_dev->rows; i++) {
for (j = 0; j < tcm->tcm_dev->cols; j++) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%d ",
data_ptr[i * tcm->tcm_dev->cols + j]);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
}
syna_tcm_buf_release(&test_data);
syna_set_bus_ref(tcm, SYNA_BUS_REF_SYSFS, false);
exit:
return count;
}
static struct kobj_attribute kobj_attr_pt_tag_moisture =
__ATTR(pt_moisture, 0444, syna_testing_pt_moisture_show, NULL);
/*
* declaration of sysfs attributes
*/
static struct attribute *attrs[] = {
&kobj_attr_check_id.attr,
&kobj_attr_pt01.attr,
&kobj_attr_pt05.attr,
&kobj_attr_pt0a.attr,
&kobj_attr_pt10.attr,
&kobj_attr_pt11.attr,
&kobj_attr_pt12.attr,
&kobj_attr_pt16.attr,
&kobj_attr_pt_tag_moisture.attr,
NULL,
};
static struct attribute_group attr_testing_group = {
.attrs = attrs,
};
/**
* syna_testing_create_dir()
*
* Create a directory and register it with sysfs.
* Then, create all defined sysfs files.
*
* @param
* [ in] tcm: the driver handle
* [ in] sysfs_dir: root directory of sysfs nodes
*
* @return
* on success, 0; otherwise, negative value on error.
*/
int syna_testing_create_dir(struct syna_tcm *tcm,
struct kobject *sysfs_dir)
{
int retval = 0;
g_testing_dir = kobject_create_and_add("testing",
sysfs_dir);
if (!g_testing_dir) {
LOGE("Fail to create testing directory\n");
return -EINVAL;
}
retval = sysfs_create_group(g_testing_dir, &attr_testing_group);
if (retval < 0) {
LOGE("Fail to create sysfs group\n");
kobject_put(g_testing_dir);
return retval;
}
g_tcm_ptr = tcm;
return 0;
}
/**
*syna_testing_remove_dir()
*
* Remove the allocate sysfs directory
*
* @param
* none
*
* @return
* on success, 0; otherwise, negative value on error.
*/
void syna_testing_remove_dir(void)
{
if (g_testing_dir) {
sysfs_remove_group(g_testing_dir, &attr_testing_group);
kobject_put(g_testing_dir);
}
}