blob: d874821fa920934f910fb938442864d8c8a7efc7 [file] [log] [blame]
/*
* cyttsp5_test_device_access_api.c
* Parade TrueTouch(TM) Standard Product V5 Device Access API test module.
* For use with Parade touchscreen controllers.
* Supported parts include:
* CYTMA5XX
* CYTMA448
* CYTMA445A
* CYTT21XXX
* CYTT31XXX
*
* Copyright (C) 2015 Parade Technologies
* Copyright (C) 2012-2015 Cypress Semiconductor
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, and only version 2, as published by the
* Free Software Foundation.
*
* 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.
*
* Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
*
*/
#include <linux/module.h>
#include "cyttsp5_device_access-api.h"
#include <asm/unaligned.h>
#define BUFFER_SIZE 256
#define COMMAND_GET_SYSTEM_INFO 2
#define COMMAND_SUSPEND_SCANNING 3
#define COMMAND_RESUME_SCANNING 4
#define COMMAND_GET_PARAMETER 5
#define COMMAND_SET_PARAMETER 6
#define PARAMETER_ACTIVE_DISTANCE_2 0x0B
struct tt_output_report {
__le16 reg_address;
__le16 length;
u8 report_id;
u8 reserved;
u8 command;
u8 parameters[0];
} __packed;
struct tt_input_report {
__le16 length;
u8 report_id;
u8 reserved;
u8 command;
u8 return_data[0];
} __packed;
static int prepare_tt_output_report(struct tt_output_report *out,
u16 length, u8 command)
{
put_unaligned_le16(0x04, &out->reg_address);
put_unaligned_le16(5 + length, &out->length);
out->report_id = 0x2f;
out->reserved = 0x00;
out->command = command;
return 7 + length;
}
static int check_and_parse_tt_input_report(struct tt_input_report *in,
u16 *length, u8 *command)
{
if (in->report_id != 0x1f)
return -EINVAL;
*length = get_unaligned_le16(&in->length);
*command = in->command & 0x7f;
return 0;
}
static int prepare_get_system_info_report(u8 *buf)
{
struct tt_output_report *out = (struct tt_output_report *)buf;
return prepare_tt_output_report(out, 0, COMMAND_GET_SYSTEM_INFO);
}
static int check_get_system_info_response(u8 *buf, u16 read_length)
{
struct tt_input_report *in = (struct tt_input_report *)buf;
u16 length = 0;
u8 command = 0;
if (read_length != 51)
return -EINVAL;
if (check_and_parse_tt_input_report(in, &length, &command)
|| command != COMMAND_GET_SYSTEM_INFO
|| length != 51)
return -EINVAL;
pr_info("PIP Major Version: %d\n", in->return_data[0]);
pr_info("PIP Minor Version: %d\n", in->return_data[1]);
pr_info("Touch Firmware Product Id: %d\n",
get_unaligned_le16(&in->return_data[2]));
pr_info("Touch Firmware Major Version: %d\n", in->return_data[4]);
pr_info("Touch Firmware Minor Version: %d\n", in->return_data[5]);
pr_info("Touch Firmware Internal Revision Control Number: %d\n",
get_unaligned_le32(&in->return_data[6]));
pr_info("Customer Specified Firmware/Configuration Version: %d\n",
get_unaligned_le16(&in->return_data[10]));
pr_info("Bootloader Major Version: %d\n", in->return_data[12]);
pr_info("Bootloader Minor Version: %d\n", in->return_data[13]);
pr_info("Family ID: 0x%02x\n", in->return_data[14]);
pr_info("Revision ID: 0x%02x\n", in->return_data[15]);
pr_info("Silicon ID: 0x%02x\n",
get_unaligned_le16(&in->return_data[16]));
pr_info("Parade Manufacturing ID[0]: 0x%02x\n", in->return_data[18]);
pr_info("Parade Manufacturing ID[1]: 0x%02x\n", in->return_data[19]);
pr_info("Parade Manufacturing ID[2]: 0x%02x\n", in->return_data[20]);
pr_info("Parade Manufacturing ID[3]: 0x%02x\n", in->return_data[21]);
pr_info("Parade Manufacturing ID[4]: 0x%02x\n", in->return_data[22]);
pr_info("Parade Manufacturing ID[5]: 0x%02x\n", in->return_data[23]);
pr_info("Parade Manufacturing ID[6]: 0x%02x\n", in->return_data[24]);
pr_info("Parade Manufacturing ID[7]: 0x%02x\n", in->return_data[25]);
pr_info("POST Result Code: 0x%02x\n",
get_unaligned_le16(&in->return_data[26]));
pr_info("Number of X Electrodes: %d\n", in->return_data[28]);
pr_info("Number of Y Electrodes: %d\n", in->return_data[29]);
pr_info("Panel X Axis Length: %d\n",
get_unaligned_le16(&in->return_data[30]));
pr_info("Panel Y Axis Length: %d\n",
get_unaligned_le16(&in->return_data[32]));
pr_info("Panel X Axis Resolution: %d\n",
get_unaligned_le16(&in->return_data[34]));
pr_info("Panel Y Axis Resolution: %d\n",
get_unaligned_le16(&in->return_data[36]));
pr_info("Panel Pressure Resolution: %d\n",
get_unaligned_le16(&in->return_data[38]));
pr_info("X_ORG: %d\n", in->return_data[40]);
pr_info("Y_ORG: %d\n", in->return_data[41]);
pr_info("Panel ID: %d\n", in->return_data[42]);
pr_info("Buttons: 0x%02x\n", in->return_data[43]);
pr_info("BAL SELF MC: 0x%02x\n", in->return_data[44]);
pr_info("Max Number of Touch Records per Refresh Cycle: %d\n",
in->return_data[45]);
return 0;
}
static int prepare_get_parameter_report(u8 *buf, u8 parameter_id)
{
struct tt_output_report *out = (struct tt_output_report *)buf;
out->parameters[0] = parameter_id;
return prepare_tt_output_report(out, 1, COMMAND_GET_PARAMETER);
}
static int check_get_parameter_response(u8 *buf, u16 read_length,
u32 *parameter_value)
{
struct tt_input_report *in = (struct tt_input_report *)buf;
u16 length = 0;
u8 command = 0;
u32 param_value = 0;
u8 param_id;
u8 param_size = 0;
if (read_length != 8 && read_length != 9 && read_length != 11)
return -EINVAL;
if (check_and_parse_tt_input_report(in, &length, &command)
|| command != COMMAND_GET_PARAMETER
|| (length != 8 && length != 9 && length != 11))
return -EINVAL;
param_id = in->return_data[0];
param_size = in->return_data[1];
if (param_size == 1)
param_value = in->return_data[2];
else if (param_size == 2)
param_value = get_unaligned_le16(&in->return_data[2]);
else if (param_size == 4)
param_value = get_unaligned_le32(&in->return_data[2]);
else
return -EINVAL;
pr_info("%s: Parameter ID: 0x%02x Value: 0x%02x\n",
__func__, param_id, param_value);
if (parameter_value)
*parameter_value = param_value;
return 0;
}
static int prepare_set_parameter_report(u8 *buf, u8 parameter_id,
u8 parameter_size, u32 parameter_value)
{
struct tt_output_report *out = (struct tt_output_report *)buf;
out->parameters[0] = parameter_id;
out->parameters[1] = parameter_size;
if (parameter_size == 1)
out->parameters[2] = (u8)parameter_value;
else if (parameter_size == 2)
put_unaligned_le16(parameter_value, &out->parameters[2]);
else if (parameter_size == 4)
put_unaligned_le32(parameter_value, &out->parameters[2]);
else
return -EINVAL;
return prepare_tt_output_report(out, 2 + parameter_size,
COMMAND_SET_PARAMETER);
}
static int check_set_parameter_response(u8 *buf, u16 read_length)
{
struct tt_input_report *in = (struct tt_input_report *)buf;
u16 length = 0;
u8 command = 0;
u8 param_id;
u8 param_size = 0;
if (read_length != 7)
return -EINVAL;
if (check_and_parse_tt_input_report(in, &length, &command)
|| command != COMMAND_SET_PARAMETER
|| length != 7)
return -EINVAL;
param_id = in->return_data[0];
param_size = in->return_data[1];
pr_info("%s: Parameter ID: 0x%02x Size: 0x%02x\n",
__func__, param_id, param_size);
return 0;
}
static int prepare_suspend_scanning_report(u8 *buf)
{
struct tt_output_report *out = (struct tt_output_report *)buf;
return prepare_tt_output_report(out, 0, COMMAND_SUSPEND_SCANNING);
}
static int check_suspend_scanning_response(u8 *buf, u16 read_length)
{
struct tt_input_report *in = (struct tt_input_report *)buf;
u16 length = 0;
u8 command = 0;
if (read_length != 5)
return -EINVAL;
if (check_and_parse_tt_input_report(in, &length, &command)
|| command != COMMAND_SUSPEND_SCANNING
|| length != 5)
return -EINVAL;
return 0;
}
static int prepare_resume_scanning_report(u8 *buf)
{
struct tt_output_report *out = (struct tt_output_report *)buf;
return prepare_tt_output_report(out, 0, COMMAND_RESUME_SCANNING);
}
static int check_resume_scanning_response(u8 *buf, u16 read_length)
{
struct tt_input_report *in = (struct tt_input_report *)buf;
u16 length = 0;
u8 command = 0;
if (read_length != 5)
return -EINVAL;
if (check_and_parse_tt_input_report(in, &length, &command)
|| command != COMMAND_RESUME_SCANNING
|| length != 5)
return -EINVAL;
return 0;
}
void cyttsp5_user_command_async_cont(const char *core_name,
u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
u16 actual_read_length, int rc)
{
if (rc) {
pr_err("%s: suspend scan fails\n", __func__);
goto exit;
}
rc = check_suspend_scanning_response(read_buf, actual_read_length);
if (rc) {
pr_err("%s: check suspend scanning response fails\n", __func__);
goto exit;
}
pr_info("%s: suspend scanning succeeds\n", __func__);
exit:
return;
}
/* Read and write buffers */
static u8 write_buf[BUFFER_SIZE];
static u8 read_buf[BUFFER_SIZE];
static uint active_distance;
module_param(active_distance, uint, 0);
static int __init cyttsp5_test_device_access_api_init(void)
{
u32 initial_active_distance;
u16 actual_read_len;
int write_len;
int rc;
pr_info("%s: Enter\n", __func__);
/* CASE 1: Run get system information */
write_len = prepare_get_system_info_report(write_buf);
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
read_buf, write_len, write_buf,
&actual_read_len);
if (rc)
goto exit;
rc = check_get_system_info_response(read_buf, actual_read_len);
if (rc)
goto exit;
/* CASE 2: Run get parameter (Active distance) */
write_len = prepare_get_parameter_report(write_buf,
PARAMETER_ACTIVE_DISTANCE_2);
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
read_buf, write_len, write_buf,
&actual_read_len);
if (rc)
goto exit;
rc = check_get_parameter_response(read_buf, actual_read_len,
&initial_active_distance);
if (rc)
goto exit;
pr_info("%s: Initial Active Distance: %d\n",
__func__, initial_active_distance);
/* CASE 3: Run set parameter (Active distance) */
write_len = prepare_set_parameter_report(write_buf,
PARAMETER_ACTIVE_DISTANCE_2, 1,
active_distance);
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
read_buf, write_len, write_buf,
&actual_read_len);
if (rc)
goto exit;
rc = check_set_parameter_response(read_buf, actual_read_len);
if (rc)
goto exit;
pr_info("%s: Active Distance set to %d\n", __func__, active_distance);
/* CASE 4: Run get parameter (Active distance) */
write_len = prepare_get_parameter_report(write_buf,
PARAMETER_ACTIVE_DISTANCE_2);
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
read_buf, write_len, write_buf,
&actual_read_len);
if (rc)
goto exit;
rc = check_get_parameter_response(read_buf, actual_read_len,
&active_distance);
if (rc)
goto exit;
pr_info("%s: New Active Distance: %d\n", __func__, active_distance);
/* CASE 5: Run suspend scanning asynchronously */
write_len = prepare_suspend_scanning_report(write_buf);
preempt_disable();
rc = cyttsp5_device_access_user_command_async(NULL,
sizeof(read_buf), read_buf, write_len, write_buf,
cyttsp5_user_command_async_cont);
preempt_enable();
if (rc)
goto exit;
exit:
return rc;
}
module_init(cyttsp5_test_device_access_api_init);
static void __exit cyttsp5_test_device_access_api_exit(void)
{
u16 actual_read_len;
int write_len;
int rc;
/* CASE 6: Run resume scanning */
write_len = prepare_resume_scanning_report(write_buf);
rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf),
read_buf, write_len, write_buf,
&actual_read_len);
if (rc)
goto exit;
rc = check_resume_scanning_response(read_buf, actual_read_len);
if (rc)
goto exit;
pr_info("%s: resume scanning succeeds\n", __func__);
exit:
return;
}
module_exit(cyttsp5_test_device_access_api_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver API Tester");
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");