blob: 466a946289db4423233114aca186dd89cedcab3d [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* pt_core.c
* Parade TrueTouch(TM) Standard Product Core Module.
* For use with Parade touchscreen controllers.
* Supported parts include:
* TMA5XX
* TMA448
* TMA445A
* TT21XXX
* TT31XXX
* TT4XXXX
* TT7XXX
* TC3XXX
*
* Copyright (C) 2015-2020 Parade Technologies
*
* 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 "pt_regs.h"
#include <linux/kthread.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/input/mt.h>
#include "nanohub_exports.h"
#include "mcu_touch.h"
#ifdef PT_PTSBC_SUPPORT
#define PT_CORE_PROBE_STARTUP_DELAY_MS 500
#endif /* PT_PTSBC_SUPPORT */
#define PT_CORE_STARTUP_RETRY_COUNT 3
#define PT_REPORT_TOUCH_EVENT_DELAY_TIME_MS 80
MODULE_FIRMWARE(PT_FW_FILE_NAME);
static const char *pt_driver_core_name = PT_CORE_NAME;
static const char *pt_driver_core_version = PT_DRIVER_VERSION;
static const char *pt_driver_core_date = PT_DRIVER_DATE;
struct pt_input_event touch_event_list_head;
struct workqueue_struct *pt_touch_event_delay_wq;
struct pt_hid_field {
int report_count;
int report_size;
int size; /* report_count * report_size */
int offset;
int data_type;
int logical_min;
int logical_max;
/* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */
u32 usage_page;
u32 collection_usage_pages[PT_HID_MAX_COLLECTIONS];
struct pt_hid_report *report;
bool record_field;
};
struct pt_hid_report {
u8 id;
u8 type;
int size;
struct pt_hid_field *fields[PT_HID_MAX_FIELDS];
int num_fields;
int record_field_index;
int header_size;
int record_size;
u32 usage_page;
int log_collection_num;
};
struct atten_node {
struct list_head node;
char *id;
struct device *dev;
int (*func)(struct device *dev);
int mode;
};
struct param_node {
struct list_head node;
u8 id;
u32 value;
u8 size;
};
struct module_node {
struct list_head node;
struct pt_module *module;
void *data;
};
struct pt_hid_cmd {
__le16 descriptor;
u8 opcode;
u8 report_type;
union {
u8 report_id;
u8 power_state;
};
u8 has_data_register;
size_t write_length;
size_t read_length;
u8 *write_buf;
u8 *read_buf;
u8 wait_interrupt;
u8 reset_cmd;
u16 timeout_ms;
};
struct pt_hid_output {
u8 cmd_type;
u16 length;
u8 command_code;
size_t write_length;
u8 *write_buf;
};
struct pt_pip1_cmd {
u8 cmd_type;
u8 command_code;
u8 reset_expected;
u16 write_length;
u8 *write_buf;
u8 novalidate;
u16 timeout_ms;
};
struct pt_pip2_cmd {
u8 cmd_id;
u8 cmd_tag_seq;
u8 *report_data;
u16 report_len;
u8 *read_buf; /* to be removed */
u16 actual_read_len; /* to be removed */
u16 timeout_ms;
u8 novalidate;
};
struct pt_pip3_cmd {
u8 cmd_tag_seq;
u8 cmd_id;
u8 reset_expected;
u8 *param;
u16 param_len;
u16 timeout_ms;
};
struct pt_raw_cmd {
u8 *write_buf;
u16 write_length;
u8 *read_buf;
u16 read_len;
u16 actual_read_len;
};
#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode)
#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4))
#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id)
#define CREATE_PIP1_FW_CMD(command) \
.cmd_type = PIP1_CMD_TYPE_FW, \
.command_code = command
#define CREATE_PIP1_BL_CMD(command) \
.cmd_type = PIP1_CMD_TYPE_BL, \
.command_code = command
#define INIT_HID_OUTPUT_FROM_PIP_CMD(pip1_cmd) \
.cmd_type = pip1_cmd->cmd_type, \
.command_code = pip1_cmd->command_code, \
.write_length = pip1_cmd->write_length, \
.write_buf = pip1_cmd->write_buf
#define INIT_HID_SET_REPORT_HEADER \
.opcode = HID_CMD_SET_REPORT, \
.report_type = 2, /* HID_FEATURE_REPORT */ \
.report_id = PT_HID_OUTPUT_REPORT_ID, \
.has_data_register = 1 /* Data register is used */
#define PT_IS_HID_WRAP_PIP_REPORT(id) \
((id) == PT_HID_WRAP_PIP_REPORT_ID)
#define PT_MAX_PR_BUF_SIZE 2048
void queue_input_event(uint16_t type, uint16_t code, int32_t value, ktime_t timestamp)
{
struct pt_input_event *input_report_event =
kmalloc(sizeof(struct pt_input_event), GFP_KERNEL);
input_report_event->type = type;
input_report_event->code = code;
input_report_event->value = value;
input_report_event->timestamp = timestamp;
list_add_tail(&input_report_event->list, &touch_event_list_head.list);
}
/*******************************************************************************
* FUNCTION: pt_pr_buf
*
* SUMMARY: Print out the contents of a buffer to kmsg based on the debug level
*
* RETURN: Void
*
* PARAMETERS:
* *dev - pointer to Device structure
* debug_level - requested debug level to print at
* *buf - pointer to buffer to print
* buf_len - size of buf
* *data_name - Descriptive name of data prefixed to data
******************************************************************************/
void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf,
u16 buf_len, const char *data_name)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int i;
int pr_buf_index = 0;
int max_size;
/* only proceed if valid debug level and there is data to print */
if (debug_level <= cd->debug_level && buf_len > 0) {
char *pr_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL);
if (!pr_buf)
return;
/*
* With spaces each printed char takes 3 bytes, subtract
* the length of the data_name and length prefix and divide 3
*/
pr_buf_index +=
scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ", data_name, buf_len);
max_size = (PT_MAX_PR_BUF_SIZE - pr_buf_index) / 3;
for (i = 0; i < buf_len && i < max_size; i++)
pr_buf_index += scnprintf(pr_buf + pr_buf_index,
PT_MAX_PR_BUF_SIZE - pr_buf_index, "%02X ",
buf[i]);
pt_debug(dev, debug_level, "%s\n", pr_buf);
kfree(pr_buf);
}
}
EXPORT_SYMBOL_GPL(pt_pr_buf);
#ifdef TTHE_TUNER_SUPPORT
/*******************************************************************************
* FUNCTION: tthe_print
*
* SUMMARY: Format data name and time stamp as the header and format the
* content of input buffer with hex base to "tthe_buf". And then wake up event
* semaphore for tthe debugfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *buf - pointer to input buffer
* buf_len - size of input buffer
* *data_name - pointer to data name
******************************************************************************/
static int tthe_print(struct pt_core_data *cd, u8 *buf, int buf_len,
const u8 *data_name)
{
int name_len = strlen(data_name);
int i, n;
u8 *p;
int remain;
u8 data_name_with_time_stamp[100];
/* Prepend timestamp, if requested, to data_name */
if (cd->show_timestamp) {
scnprintf(data_name_with_time_stamp, sizeof(data_name_with_time_stamp), "[%u] %s",
pt_get_time_stamp(), data_name);
data_name = data_name_with_time_stamp;
name_len = strlen(data_name);
}
mutex_lock(&cd->tthe_lock);
if (!cd->tthe_buf)
goto exit;
/* Add 1 due to the '\n' that is appended at the end */
if (cd->tthe_buf_len + name_len + buf_len + 1 > cd->tthe_buf_size)
goto exit;
if (name_len + buf_len == 0)
goto exit;
remain = cd->tthe_buf_size - cd->tthe_buf_len;
if (remain < name_len)
name_len = remain;
p = cd->tthe_buf + cd->tthe_buf_len;
memcpy(p, data_name, name_len);
cd->tthe_buf_len += name_len;
p += name_len;
remain -= name_len;
*p = 0;
for (i = 0; i < buf_len; i++) {
n = scnprintf(p, remain, "%02X ", buf[i]);
if (n <= 0)
break;
p += n;
remain -= n;
cd->tthe_buf_len += n;
}
n = scnprintf(p, remain, "\n");
cd->tthe_buf_len += n;
exit:
wake_up(&cd->wait_q);
mutex_unlock(&cd->tthe_lock);
return 0;
}
/*******************************************************************************
* FUNCTION: _pt_request_tthe_print
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request to print data to the "tthe_buffer".
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int _pt_request_tthe_print(struct device *dev, u8 *buf,
int buf_len, const u8 *data_name)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return tthe_print(cd, buf, buf_len, data_name);
}
#endif
/*******************************************************************************
* FUNCTION: pt_platform_detect_read
*
* SUMMARY: To be passed to platform dectect function to perform a read
* operation.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to Device structure
* *buf - pointer to buffer where the data read will be stored
* size - size to be read
******************************************************************************/
static int pt_platform_detect_read(struct device *dev, void *buf, int size)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return pt_adap_read_default(cd, buf, size);
}
/*******************************************************************************
* FUNCTION: pt_add_parameter
*
* SUMMARY: Adds a parameter that has been altered to the parameter linked list.
* On every reset of the DUT this linked list is traversed and all
* parameters in it are restored to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* param_id - parameter ID to add
* param_value - Value corresponding to the ID
* param_size - Size of param_value
******************************************************************************/
static int pt_add_parameter(struct pt_core_data *cd,
u8 param_id, u32 param_value, u8 param_size)
{
struct param_node *param, *param_new;
/* Check if parameter already exists in the list */
spin_lock(&cd->spinlock);
list_for_each_entry(param, &cd->param_list, node) {
if (param->id == param_id) {
/* Update parameter */
param->value = param_value;
pt_debug(cd->dev, DL_INFO,
"%s: Update parameter id:%d value:%d size:%d\n",
__func__, param_id, param_value, param_size);
goto exit_unlock;
}
}
spin_unlock(&cd->spinlock);
param_new = kzalloc(sizeof(*param_new), GFP_KERNEL);
if (!param_new)
return -ENOMEM;
param_new->id = param_id;
param_new->value = param_value;
param_new->size = param_size;
pt_debug(cd->dev, DL_INFO,
"%s: Add parameter id:%d value:%d size:%d\n",
__func__, param_id, param_value, param_size);
spin_lock(&cd->spinlock);
list_add(&param_new->node, &cd->param_list);
exit_unlock:
spin_unlock(&cd->spinlock);
return 0;
}
#ifndef TTDL_KERNEL_SUBMISSION
#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
* FUNCTION: pt_erase_parameter_list
*
* SUMMARY: Empty out the entire parameter linked list of all parameter/value
* pairs. In some test cases this functionality is needed to ensure DUT
* returns to a virgin state after a reset and no parameters are restored.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_erase_parameter_list(struct pt_core_data *cd)
{
struct param_node *pos, *temp;
spin_lock(&cd->spinlock);
list_for_each_entry_safe(pos, temp, &cd->param_list, node) {
pt_debug(cd->dev, DL_INFO,
"%s: Parameter Restore List - remove 0x%02x\n",
__func__, pos->id);
list_del(&pos->node);
kfree(pos);
}
spin_unlock(&cd->spinlock);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_count_parameter_list
*
* SUMMARY: Count the items in the RAM parameter restor list
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_count_parameter_list(struct pt_core_data *cd)
{
struct param_node *pos, *temp;
int entries = 0;
spin_lock(&cd->spinlock);
list_for_each_entry_safe(pos, temp, &cd->param_list, node)
entries++;
spin_unlock(&cd->spinlock);
return entries;
}
#endif /* TTDL_DIAGNOSTICS */
#endif /* !TTDL_KERNEL_SUBMISSION */
/*******************************************************************************
* FUNCTION: request_exclusive
*
* SUMMARY: Request exclusive access to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *ownptr - pointer to device
* timeout_ms - Timeout value
******************************************************************************/
int request_exclusive(struct pt_core_data *cd, void *ownptr,
int timeout_ms)
{
int t = msecs_to_jiffies(timeout_ms);
bool with_timeout = (timeout_ms != 0);
pt_debug(cd->dev, DL_DEBUG, "%s: Attempt to Request EXCLUSIVE t=%d\n", __func__,
timeout_ms);
mutex_lock(&cd->system_lock);
if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
cd->exclusive_dev = ownptr;
goto exit;
}
cd->exclusive_waits++;
wait:
mutex_unlock(&cd->system_lock);
if (with_timeout) {
t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
if (IS_TMO(t)) {
pt_debug(cd->dev, DL_ERROR,
"%s: tmo waiting exclusive access\n", __func__);
return -ETIME;
}
} else {
wait_event(cd->wait_q, !cd->exclusive_dev);
}
mutex_lock(&cd->system_lock);
if (cd->exclusive_dev)
goto wait;
cd->exclusive_dev = ownptr;
cd->exclusive_waits--;
exit:
mutex_unlock(&cd->system_lock);
pt_debug(cd->dev, DL_DEBUG, "%s: request exclusive ok=%p\n",
__func__, ownptr);
return 0;
}
/*******************************************************************************
* FUNCTION: release_exclusive_
*
* SUMMARY: Release exclusive access to the DUT
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *cd - pointer to core data
* *ownptr - pointer to device
******************************************************************************/
static int release_exclusive_(struct pt_core_data *cd, void *ownptr)
{
pt_debug(cd->dev, DL_DEBUG, "%s: Attempt to Release EXCLUSIVE\n", __func__);
if (cd->exclusive_dev != ownptr)
return -EINVAL;
pt_debug(cd->dev, DL_DEBUG, "%s: exclusive_dev %p freed\n",
__func__, cd->exclusive_dev);
cd->exclusive_dev = NULL;
wake_up(&cd->wait_q);
return 0;
}
/*******************************************************************************
* FUNCTION: release_exclusive
*
* SUMMARY: Protected wrapper to release_exclusive_()
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *ownptr - pointer to device
******************************************************************************/
int release_exclusive(struct pt_core_data *cd, void *ownptr)
{
int rc;
mutex_lock(&cd->system_lock);
rc = release_exclusive_(cd, ownptr);
mutex_unlock(&cd->system_lock);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_write_to_hid_cmd_reg_
*
* SUMMARY: Send the HID command to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_cmd - pointer to the HID command to write to command register
******************************************************************************/
static int pt_write_to_hid_cmd_reg_(struct pt_core_data *cd,
struct pt_hid_cmd *hid_cmd)
{
int rc = 0;
u8 *cmd;
u16 cmd_length;
u8 cmd_offset = 0;
if (hid_cmd->descriptor) {
cmd_length = 2; /* hid or report register */
} else {
cmd_length =
2 /* command register */
+ 2 /* command */
+ (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */
+ (hid_cmd->has_data_register ? 2 : 0) /* Data register */
+ hid_cmd->write_length; /* Data length */
}
cmd = kzalloc(cmd_length, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
/* hid & report descriptor doesn't require other field */
if (hid_cmd->descriptor) {
memcpy(&cmd[cmd_offset], &hid_cmd->descriptor, cmd_length);
goto skip_other_field;
}
/* Set Command register */
memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register,
sizeof(cd->hid_desc.command_register));
cmd_offset += sizeof(cd->hid_desc.command_register);
/* Set Command */
SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type);
if (hid_cmd->report_id >= 0XF)
SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF);
else
SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id);
cmd_offset++;
SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode);
cmd_offset++;
if (hid_cmd->report_id >= 0XF) {
cmd[cmd_offset] = hid_cmd->report_id;
cmd_offset++;
}
/* Set Data register */
if (hid_cmd->has_data_register) {
memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register,
sizeof(cd->hid_desc.data_register));
cmd_offset += sizeof(cd->hid_desc.data_register);
}
if (hid_cmd->opcode == HID_CMD_GET_REPORT)
goto skip_other_field;
/* Set Data */
if (hid_cmd->write_length && hid_cmd->write_buf) {
memcpy(&cmd[cmd_offset], hid_cmd->write_buf,
hid_cmd->write_length);
cmd_offset += hid_cmd->write_length;
}
pt_debug(cd->dev, DL_DEBUG, ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", __func__,
cmd_length, cmd[HID_CMD_COMMAND_ID_OFFSET]);
skip_other_field:
pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> ATM - HID CMD");
rc = pt_adap_write_read_specific(cd, cmd_length, cmd, hid_cmd->read_buf,
hid_cmd->read_length);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Fail pt_adap_transfer\n", __func__);
kfree(cmd);
return rc;
}
#ifdef TTDL_DIAGNOSTICS
/*******************************************************************************
* FUNCTION: pt_toggle_err_gpio
*
* SUMMARY: Toggles the pre-defined error GPIO
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data
* type - type of err that occured
******************************************************************************/
void pt_toggle_err_gpio(struct pt_core_data *cd, u8 type)
{
pt_debug(cd->dev, DL_DEBUG, "%s called with type = %d\n",
__func__, type);
if (cd->err_gpio && type == cd->err_gpio_type) {
pt_debug(cd->dev, DL_WARN, "%s: Toggle ERR GPIO\n", __func__);
gpio_direction_output(cd->err_gpio,
!gpio_get_value(cd->err_gpio));
}
}
/*******************************************************************************
* FUNCTION: _pt_request_toggle_err_gpio
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request to toggle the err_gpio
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data
* type - type of err that occured
******************************************************************************/
void _pt_request_toggle_err_gpio(struct device *dev, u8 type)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_toggle_err_gpio(cd, type);
}
#endif /* TTDL_DIAGNOSTICS */
/*******************************************************************************
* FUNCTION: pt_hid_send_cmd_and_wait_
*
* SUMMARY: Send the HID command to the DUT and wait for the response
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_cmd - pointer to the HID command to send
******************************************************************************/
static int pt_hid_send_cmd_and_wait_(struct pt_core_data *cd,
struct pt_hid_cmd *hid_cmd)
{
int rc = 0;
int t;
u16 timeout_ms;
int *cmd_state;
u16 max_timeout_ms = PT_HID_MULTIPACKET_TIMEOUT;
if (hid_cmd->reset_cmd)
cmd_state = &cd->hid_reset_cmd_state;
else
cmd_state = &cd->hid_cmd_state;
mutex_lock(&cd->system_lock);
*cmd_state = 1;
mutex_unlock(&cd->system_lock);
rc = pt_write_to_hid_cmd_reg_(cd, hid_cmd);
if (rc)
goto error;
if (hid_cmd->timeout_ms)
timeout_ms = hid_cmd->timeout_ms;
else
timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT;
again:
t = wait_event_timeout(cd->wait_q, (*cmd_state == 0),
msecs_to_jiffies(timeout_ms));
if (IS_TMO(t)) {
if ((cd->pt_hid_buf_op.last_remain_packet != 0) &&
(max_timeout_ms > timeout_ms)) {
max_timeout_ms -= timeout_ms;
goto again;
}
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR,
"%s: HID output cmd execution timed out\n",
__func__);
rc = -ETIME;
goto error;
}
goto exit;
error:
mutex_lock(&cd->system_lock);
*cmd_state = 0;
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_send_cmd_no_wait_
*
* SUMMARY: The function works to send HID command and can read response
* directly instead of waiting it to be recevied in interrupt function. It
* assgins the read buffer to receive response in following condition:
* 1) descriptor is assigned to get hid descriptor or report descripter
* 2) output register is assigned for vendor-defined commands (TBD)
*
* NOTE: If no read buffer is assigned, it only perform send action.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_cmd - pointer to the HID command to send
******************************************************************************/
static int pt_hid_send_cmd_no_wait_(struct pt_core_data *cd,
struct pt_hid_cmd *hid_cmd)
{
int rc = 0;
if (hid_cmd->descriptor ||
hid_cmd->opcode == HID_CMD_GET_REPORT)
hid_cmd->read_buf = cd->response_buf;
rc = pt_write_to_hid_cmd_reg_(cd, hid_cmd);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_send_command_
*
* SUMMARY: Wrapper function to call pt_hid_send_cmd_no_wait_() for HID
* protocol and pt_hid_send_cmd_and_wait_() for PIP protocol.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_cmd - pointer to the HID command to send
******************************************************************************/
static int pt_hid_send_command_(struct pt_core_data *cd,
struct pt_hid_cmd *hid_cmd)
{
if (cd->protocol_mode != PT_PROTOCOL_MODE_PIP &&
!cd->dual_mcu_available)
return pt_hid_send_cmd_no_wait_(cd, hid_cmd);
else
return pt_hid_send_cmd_and_wait_(cd, hid_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_send_user_cmd_and_wait_
*
* SUMMARY: Send HID user command to the DUT and wait for the response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_cmd - pointer to HID command data structure
******************************************************************************/
static int pt_hid_send_user_cmd_and_wait_(struct pt_core_data *cd,
struct pt_hid_cmd *hid_cmd)
{
int rc = 0;
int t;
u8 cmd_id = 0;
u16 max_timeout_ms = PT_HID_MULTIPACKET_TIMEOUT;
mutex_lock(&cd->system_lock);
/*
* The data in write_buf refers to the data in HID command offset 6 ~ N,
* so the command id offset in write_buf is 6.
*/
cmd_id = hid_cmd->write_buf[6] & 0x7F;
cd->hid_cmd_state = cmd_id + 1;
mutex_unlock(&cd->system_lock);
rc = pt_write_to_hid_cmd_reg_(cd, hid_cmd);
if (rc)
goto error;
if (cmd_id == HID_CMD_ID_START_BOOTLOADER
&& cd->dual_mcu_available) {
pt_debug(cd->dev, DL_INFO,
"%s: Dual MCU is enabled, no BL sentinel after 0x31 cmd\n",
__func__);
usleep_range(10000, 11000);
goto error;
}
again:
t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT));
if (IS_TMO(t)) {
if ((cd->pt_hid_buf_op.last_remain_packet != 0) &&
(max_timeout_ms > PT_HID_CMD_DEFAULT_TIMEOUT)) {
max_timeout_ms -= PT_HID_CMD_DEFAULT_TIMEOUT;
goto again;
}
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR,
"%s: HID output cmd execution timed out\n",
__func__);
rc = -ETIME;
goto error;
}
goto exit;
error:
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = 0;
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_user_cmd_send_
*
* SUMMARY: Load HID payload data into a HID cmd structure and send it to DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_pl_buf - pointer to HID cmd payload data
* pl_buf_len - HID cmd payload data length
******************************************************************************/
static int pt_hid_user_cmd_send_(struct pt_core_data *cd,
u8 *hid_pl_buf, u8 pl_buf_len)
{
struct pt_hid_cmd hid_cmd = {
INIT_HID_SET_REPORT_HEADER,
};
int rc = 0;
u16 index = 0;
/*
* 'write_length' is calculated by:
* Length field(2) +
* REPORT_ID(1) +
* Payload (hid_max_output_len)
*/
hid_cmd.write_length = 3 + cd->hid_core.hid_max_output_len;
if (hid_cmd.write_length < (pl_buf_len + 2))
return -EINVAL;
hid_cmd.write_buf = kzalloc(hid_cmd.write_length, GFP_KERNEL);
if (!hid_cmd.write_buf)
return -ENOMEM;
/* Set Length Field(2) */
hid_cmd.write_buf[index++] = LOW_BYTE(hid_cmd.write_length);
hid_cmd.write_buf[index++] = HI_BYTE(hid_cmd.write_length);
/* Copy hid palyload data */
memcpy(&hid_cmd.write_buf[index], hid_pl_buf,
pl_buf_len);
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, hid_pl_buf, pl_buf_len, "HID_CMD=");
#endif
rc = pt_hid_send_user_cmd_and_wait_(cd, &hid_cmd);
kfree(hid_cmd.write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_user_cmd_send
*
* SUMMARY: Protected call to pt_hid_user_cmd_send_ by exclusive access to
* the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_pl_buf - pointer to HID cmd payload data
* pl_buf_len - HID cmd payload data length
******************************************************************************/
static int pt_hid_user_cmd_send(struct pt_core_data *cd,
u8 *hid_pl_buf, u8 pl_buf_len)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_user_cmd_send_(cd, hid_pl_buf, pl_buf_len);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_cmd_reset_
*
* SUMMARY: Send the HID RESET command to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_cmd_reset_(struct pt_core_data *cd)
{
struct pt_hid_cmd hid_cmd = {
.opcode = HID_CMD_RESET,
.reset_cmd = 1,
.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
};
return pt_hid_send_command_(cd, &hid_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_cmd_reset
*
* SUMMARY: Wrapper function for pt_hid_cmd_reset_ that guarantees exclusive
* access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_cmd_reset(struct pt_core_data *cd)
{
int rc = 0;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
pt_debug(cd->dev, DL_INFO, "%s: Send HID Reset command\n", __func__);
rc = pt_hid_cmd_reset_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_cmd_get_report_
*
* SUMMARY: Send the HID Get report command to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* get_rpt_cmd - Get report command LSB
******************************************************************************/
static int pt_hid_cmd_get_report_(struct pt_core_data *cd,
u8 get_rpt_cmd)
{
struct pt_hid_cmd hid_cmd = {
.opcode = HID_CMD_GET_REPORT,
.has_data_register = 1,
.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
};
hid_cmd.report_type = (get_rpt_cmd >> 4) & 0x03;
hid_cmd.report_id = get_rpt_cmd & 0x0f;
return pt_hid_send_command_(cd, &hid_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_cmd_get_report
*
* SUMMARY: Wrapper function for pt_hid_cmd_get_report_ that guarantees
* exclusive access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* get_rpt_cmd - Get report command LSB
******************************************************************************/
static int pt_hid_cmd_get_report(struct pt_core_data *cd,
u8 get_rpt_cmd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_cmd_get_report_(cd, get_rpt_cmd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_cmd_set_power_
*
* SUMMARY: Send hid cmd to set power state for the DUT and wait for response
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
******************************************************************************/
static int pt_hid_cmd_set_power_(struct pt_core_data *cd,
u8 power_state)
{
int rc = 0;
struct pt_hid_cmd hid_cmd = {
.opcode = HID_CMD_SET_POWER,
.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
};
hid_cmd.power_state = power_state;
/* The chip won't give response if goes to Deep Standby */
if (power_state == HID_POWER_STANDBY) {
rc = pt_hid_send_cmd_no_wait_(cd, &hid_cmd);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Failed to set power to state:%d\n",
__func__, power_state);
else
cd->fw_sys_mode_in_standby_state = true;
return rc;
}
cd->fw_sys_mode_in_standby_state = false;
rc = pt_hid_send_command_(cd, &hid_cmd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Failed to set power to state:%d\n",
__func__, power_state);
return rc;
}
/* HID COMMAND doesn't have a response */
if (cd->protocol_mode != PT_PROTOCOL_MODE_PIP)
return rc;
/* validate */
if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID)
|| ((cd->response_buf[3] & 0x3) != power_state)
|| ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER))
rc = -EINVAL;
return rc;
}
#ifndef TTDL_KERNEL_SUBMISSION
/*******************************************************************************
* FUNCTION: pt_hid_cmd_set_power
*
* SUMMARY: Wrapper function for pt_hid_cmd_set_power_ that guarantees
* exclusive access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
******************************************************************************/
static int pt_hid_cmd_set_power(struct pt_core_data *cd,
u8 power_state)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_cmd_set_power_(cd, power_state);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
#endif /* !TTDL_KERNEL_SUBMISSION */
static const u16 crc_table[16] = {
0x0000, 0x1021, 0x2042, 0x3063,
0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b,
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
};
/*******************************************************************************
* FUNCTION: _pt_compute_crc
*
* SUMMARY: Calculate CRC by CRC table.
*
* RETURN:
* CRC calculation result
*
* PARAMETERS:
* *buf - pointer to the data array to be calculated
* size - size of data array
******************************************************************************/
static u16 _pt_compute_crc(u8 *buf, u32 size)
{
u16 remainder = 0xFFFF;
u16 xor_mask = 0x0000;
u32 index;
u32 byte_value;
u32 table_index;
u32 crc_bit_width = sizeof(u16) * 8;
/* Divide the message by polynomial, via the table. */
for (index = 0; index < size; index++) {
byte_value = buf[index];
table_index = ((byte_value >> 4) & 0x0F)
^ (remainder >> (crc_bit_width - 4));
remainder = crc_table[table_index] ^ (remainder << 4);
table_index = (byte_value & 0x0F)
^ (remainder >> (crc_bit_width - 4));
remainder = crc_table[table_index] ^ (remainder << 4);
}
/* Perform the final remainder CRC. */
return remainder ^ xor_mask;
}
u16 ccitt_Table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
};
/*******************************************************************************
* FUNCTION: crc_ccitt_calculate
*
* SUMMARY: Calculate CRC with ccitt standard by CRC table.
*
* RETURN:
* CRC calculation result
*
* PARAMETERS:
* *q - pointer to the data array to be calculated
* len - size of data array
******************************************************************************/
static unsigned short crc_ccitt_calculate(unsigned char *q, int len)
{
unsigned short crc = 0xffff;
while (len-- > 0)
crc = ccitt_Table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8);
return crc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_next_cmd_seq
*
* SUMMARY: Gets the next sequence number for a PIP2 command. The sequence
* number is a 3 bit value (bits [0-2]) but because TTDL will always have
* the TAG bit set (bit 3), the counter starts at 0x08 and goes to 0x0F.
* If the "force_pip2_seq" holds a valid seq value (0x08-0x0F) then do not
* increment, just use the forced value.
*
* RETURN: Next command sequence number [0x08-0x0F]
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static u8 pt_pip2_get_next_cmd_seq(struct pt_core_data *cd)
{
#ifdef TTDL_DIAGNOSTICS
if (cd->force_pip2_seq <= 0x07) {
cd->pip2_cmd_tag_seq++;
if (cd->pip2_cmd_tag_seq > 0x0F)
cd->pip2_cmd_tag_seq = 0x08;
} else {
cd->pip2_cmd_tag_seq = cd->force_pip2_seq;
}
#else
cd->pip2_cmd_tag_seq++;
if (cd->pip2_cmd_tag_seq > 0x0F)
cd->pip2_cmd_tag_seq = 0x08;
#endif
return cd->pip2_cmd_tag_seq;
}
/*
* Following macros are to define the response time (the interval between PIP2
* command finishes sending and INT pin falls). The unit is in microsecond.
* It has different time settings between the solution GPIO polling and Bus
* polling due to the considration for system load.
*/
#ifdef PT_POLL_RESP_BY_BUS
#define POLL_RETRY_DEFAULT_INTERVAL 50
#define PIP2_RESP_DEFAULT_TIME_MIN 50
#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
#define PIP2_RESP_FILE_WRITE_TIME_MIN 220
#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
#else
#define POLL_RETRY_DEFAULT_INTERVAL 20
#define PIP2_RESP_DEFAULT_TIME_MIN 20
#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
#define PIP2_RESP_FILE_WRITE_TIME_MIN 20
#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
#endif
/*
* id: the command id defined in PIP2
* response_len: the (maximum) length of response.
* response_time_min: minimum response time in microsecond
* response_time_max: maximum response time in microsecond
*/
static const struct pip2_cmd_response_structure pip2_cmd_response[] = {
{.id = PIP2_CMD_ID_PING,
.response_len = 255,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_STATUS,
.response_len = PIP2_EXTRA_BYTES_NUM + 5,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_CTRL,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PT_PIP2_CMD_FILE_ERASE_TIMEOUT},
{.id = PIP2_CMD_ID_CONFIG,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_CLEAR,
.response_len = PIP2_EXTRA_BYTES_NUM + 0,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_RESET,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_VERSION,
.response_len = PIP2_EXTRA_BYTES_NUM + 23,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_FILE_OPEN,
.response_len = PIP2_EXTRA_BYTES_NUM + 2,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_FILE_CLOSE,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_FILE_READ,
.response_len = 255,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_FILE_WRITE,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_FILE_WRITE_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_FILE_IOCTL,
.response_len = PIP2_EXTRA_BYTES_NUM + 10,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_FILE_IOCTL_TIME_MAX},
{.id = PIP2_CMD_ID_FLASH_INFO,
.response_len = PIP2_EXTRA_BYTES_NUM + 17,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_EXECUTE,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_GET_LAST_ERRNO,
.response_len = PIP2_EXTRA_BYTES_NUM + 3,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_EXIT_HOST_MODE,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_READ_GPIO,
.response_len = PIP2_EXTRA_BYTES_NUM + 5,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_EXECUTE_SCAN,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_SET_PARAMETER,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_GET_PARAMETER,
.response_len = PIP2_EXTRA_BYTES_NUM + 7,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_SET_DDI_REG,
.response_len = PIP2_EXTRA_BYTES_NUM + 1,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_GET_DDI_REG,
.response_len = PIP2_EXTRA_BYTES_NUM + 249,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
{.id = PIP2_CMD_ID_END,
.response_len = 255,
.response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
.response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}
};
/*******************************************************************************
* FUNCTION: pt_pip2_get_cmd_response_len
*
* SUMMARY: Gets the expected response length based on the command ID
*
* RETURN: Expected response length
*
* PARAMETERS:
* id - Command ID (-1 means input ID is not in list of PIP2 command)
******************************************************************************/
static int pt_pip2_get_cmd_response_len(u8 id)
{
const struct pip2_cmd_response_structure *p = pip2_cmd_response;
while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
p++;
if (p->id != PIP2_CMD_ID_END)
return p->response_len;
else
return -1;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_cmd_resp_time_min
*
* SUMMARY: Gets the minimum response time (the interval between PIP2 command
* finishes sending and INT pin falls) based on the command ID
*
* RETURN: Estimated minimum response time in microsecond
*
* PARAMETERS:
* id - Command ID
******************************************************************************/
static u32 pt_pip2_get_cmd_resp_time_min(u8 id)
{
const struct pip2_cmd_response_structure *p = pip2_cmd_response;
while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
p++;
if (p->id != PIP2_CMD_ID_END)
return p->response_time_min;
else
return PIP2_RESP_DEFAULT_TIME_MIN;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_cmd_resp_time_max
*
* SUMMARY: Gets the maximum response time (the interval between PIP2 command
* finishes sending and INT pin falls) based on the command ID
*
* RETURN: Estimated maximum response time in microsecond
*
* PARAMETERS:
* id - Command ID
******************************************************************************/
static u32 pt_pip2_get_cmd_resp_time_max(u8 id)
{
const struct pip2_cmd_response_structure *p = pip2_cmd_response;
while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
p++;
if (p->id != PIP2_CMD_ID_END)
return p->response_time_max;
else
return PIP2_RESP_DEFAULT_TIME_MAX;
}
static const struct PIP_vs_PURE_HID_cmd_id_map PIP1_TO_HID_CMD_ID_MAP[] = {
{.pip_cmd_id = PIP1_CMD_ID_ENTER_EASYWAKE_STATE,
.hid_cmd_id = HID_CMD_ID_ENTER_EASYWAKE_STATE},
{.pip_cmd_id = PIP1_CMD_ID_START_BOOTLOADER,
.hid_cmd_id = HID_CMD_ID_START_BOOTLOADER},
{.pip_cmd_id = PIP1_CMD_ID_GET_NOISE_METRICS,
.hid_cmd_id = HID_CMD_ID_GET_NOISE_METRICS},
{.pip_cmd_id = PIP1_CMD_ID_GET_SYSINFO,
.hid_cmd_id = HID_CMD_ID_GET_SYSINFO},
{.pip_cmd_id = PIP1_CMD_ID_SUSPEND_SCANNING,
.hid_cmd_id = HID_CMD_ID_SUSPEND_SCANNING},
{.pip_cmd_id = PIP1_CMD_ID_RESUME_SCANNING,
.hid_cmd_id = HID_CMD_ID_RESUME_SCANNING},
{.pip_cmd_id = PIP1_CMD_ID_GET_PARAM,
.hid_cmd_id = HID_CMD_ID_GET_PARAM},
{.pip_cmd_id = PIP1_CMD_ID_SET_PARAM,
.hid_cmd_id = HID_CMD_ID_SET_PARAM},
{.pip_cmd_id = PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC,
.hid_cmd_id = HID_CMD_ID_VERIFY_CONFIG_BLOCK_CRC},
{.pip_cmd_id = PIP1_CMD_ID_GET_CONFIG_ROW_SIZE,
.hid_cmd_id = HID_CMD_ID_GET_CONFIG_ROW_SIZE},
{.pip_cmd_id = PIP1_CMD_ID_READ_DATA_BLOCK,
.hid_cmd_id = HID_CMD_ID_READ_DATA_BLOCK},
{.pip_cmd_id = PIP1_CMD_ID_WRITE_DATA_BLOCK,
.hid_cmd_id = HID_CMD_ID_WRITE_DATA_BLOCK},
{.pip_cmd_id = PIP1_CMD_ID_GET_DATA_STRUCTURE,
.hid_cmd_id = HID_CMD_ID_GET_DATA_STRUCTURE},
{.pip_cmd_id = PIP1_CMD_ID_LOAD_SELF_TEST_PARAM,
.hid_cmd_id = HID_CMD_ID_LOAD_SELF_TEST_PARAM},
{.pip_cmd_id = PIP1_CMD_ID_RUN_SELF_TEST,
.hid_cmd_id = HID_CMD_ID_RUN_SELF_TEST},
{.pip_cmd_id = PIP1_CMD_ID_GET_SELF_TEST_RESULT,
.hid_cmd_id = HID_CMD_ID_GET_SELF_TEST_RESULT},
{.pip_cmd_id = PIP1_CMD_ID_CALIBRATE_IDACS,
.hid_cmd_id = HID_CMD_ID_CALIBRATE_IDACS},
{.pip_cmd_id = PIP1_CMD_ID_INITIALIZE_BASELINES,
.hid_cmd_id = HID_CMD_ID_INITIALIZE_BASELINES},
{.pip_cmd_id = PIP1_CMD_ID_EXEC_PANEL_SCAN,
.hid_cmd_id = HID_CMD_ID_EXEC_PANEL_SCAN},
{.pip_cmd_id = PIP1_CMD_ID_RETRIEVE_PANEL_SCAN,
.hid_cmd_id = HID_CMD_ID_RETRIEVE_PANEL_SCAN},
{.pip_cmd_id = PIP1_CMD_ID_START_SENSOR_DATA_MODE,
.hid_cmd_id = HID_CMD_ID_START_SENSOR_DATA_MODE},
{.pip_cmd_id = PIP1_CMD_ID_STOP_SENSOR_DATA_MODE,
.hid_cmd_id = HID_CMD_ID_STOP_SENSOR_DATA_MODE},
{.pip_cmd_id = PIP1_CMD_ID_START_TRACKING_HEATMAP_MODE,
.hid_cmd_id = HID_CMD_ID_START_TRACKING_HEATMAP_MODE},
{.pip_cmd_id = PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED,
.hid_cmd_id = HID_CMD_ID_CALIBRATE_DEVICE_EXTENDED},
{.pip_cmd_id = PIP1_CMD_ID_LAST,
.hid_cmd_id = HID_CMD_ID_END},
};
static const struct PIP_vs_PURE_HID_cmd_id_map PIP2_TO_HID_CMD_ID_MAP[] = {
{.pip_cmd_id = PIP2_CMD_ID_STATUS,
.hid_cmd_id = HID_CMD_ID_STATUS},
{.pip_cmd_id = PIP2_CMD_ID_VERSION,
.hid_cmd_id = HID_CMD_ID_VERSION},
{.pip_cmd_id = PIP2_CMD_EXECUTE_SCAN,
.hid_cmd_id = HID_CMD_ID_EXEC_PANEL_SCAN},
{.pip_cmd_id = PIP2_CMD_SET_PARAMETER,
.hid_cmd_id = HID_CMD_ID_SET_PARAM},
{.pip_cmd_id = PIP2_CMD_GET_PARAMETER,
.hid_cmd_id = HID_CMD_ID_GET_PARAM},
{.pip_cmd_id = PIP2_CMD_SET_DDI_REG,
.hid_cmd_id = HID_CMD_ID_SET_DDI_REG},
{.pip_cmd_id = PIP2_CMD_GET_DDI_REG,
.hid_cmd_id = HID_CMD_ID_GET_DDI_REG},
{.pip_cmd_id = PIP2_CMD_REALTIME_SIGNAL_MODE,
.hid_cmd_id = HID_CMD_ID_REALTIME_SIGNAL_MODE},
{.pip_cmd_id = PIP2_CMD_ID_END,
.hid_cmd_id = HID_CMD_ID_END},
};
static u8 pt_get_hid_cmd_id_from_pip(u8 id,
const struct PIP_vs_PURE_HID_cmd_id_map *id_map)
{
const struct PIP_vs_PURE_HID_cmd_id_map *p = id_map;
while ((p->pip_cmd_id != id) && (p->hid_cmd_id != HID_CMD_ID_END))
p++;
return p->hid_cmd_id;
}
static u8 pt_get_pip_cmd_id_from_hid(u8 id,
const struct PIP_vs_PURE_HID_cmd_id_map *id_map)
{
const struct PIP_vs_PURE_HID_cmd_id_map *p = id_map;
while ((p->hid_cmd_id != id) && (p->hid_cmd_id != HID_CMD_ID_END))
p++;
return p->pip_cmd_id;
}
/*******************************************************************************
* FUNCTION: pt_pip2_validate_response
*
* SUMMARY: Validate the response of PIP2 command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip2_cmd - pointer to PIP2 command structure
******************************************************************************/
static int pt_pip2_validate_response(struct pt_core_data *cd,
struct pt_pip2_cmd *pip2_cmd)
{
int rc = 0;
u8 response_seq = 0;
u8 reserved_bits = 0;
u8 cmd_id = 0;
u8 response_bit = 0;
unsigned short calc_crc = 0;
unsigned short resp_crc = 0;
/* Verify the length of response buffer */
if (pip2_cmd->actual_read_len < PT_MIN_PIP2_PACKET_SIZE) {
pt_debug(cd->dev, DL_ERROR,
"%s cmd[0x%02X] read lenght ERR: read_len = %d\n",
__func__, pip2_cmd->cmd_id,
pip2_cmd->actual_read_len);
rc = -EINVAL;
goto exit;
}
/* Verify the CRC */
calc_crc = crc_ccitt_calculate(pip2_cmd->read_buf,
pip2_cmd->actual_read_len - 2);
resp_crc = pip2_cmd->read_buf[pip2_cmd->actual_read_len - 2] << 8;
resp_crc |= pip2_cmd->read_buf[pip2_cmd->actual_read_len - 1];
if (resp_crc != calc_crc) {
pt_debug(cd->dev, DL_ERROR,
"%s: cmd[0x%02X] CRC ERR: calc=0x%04X rsp=0x%04X\n",
__func__, pip2_cmd->cmd_id, calc_crc, resp_crc);
#ifdef TTDL_DIAGNOSTICS
cd->pip2_crc_error_count++;
#endif /* TTDL_DIAGNOSTICS */
rc = -EINVAL;
goto exit;
}
/* Verify the response bit is set */
response_bit = pip2_cmd->read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x80;
if (!response_bit) {
pt_debug(cd->dev, DL_ERROR,
"%s cmd[0x%02X] response bit ERR: response_bit = %d\n",
__func__, pip2_cmd->cmd_id, response_bit);
rc = -EINVAL;
goto exit;
}
/* Verify the command ID matches from command to response */
cmd_id = pip2_cmd->read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x7F;
if (cmd_id != pip2_cmd->cmd_id) {
pt_debug(cd->dev, DL_ERROR,
"%s cmd[0x%02X] command ID ERR: cmd_id = 0x%02X\n",
__func__, pip2_cmd->cmd_id, cmd_id);
rc = -EINVAL;
goto exit;
}
/* Verify the SEQ number matches from command to response */
response_seq = pip2_cmd->read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F;
if ((pip2_cmd->cmd_tag_seq & 0x0F) != response_seq) {
pt_debug(cd->dev, DL_ERROR,
"%s cmd[0x%02X] send_seq = 0x%02X, resp_seq = 0x%02X\n",
__func__, pip2_cmd->cmd_id,
pip2_cmd->cmd_tag_seq, response_seq);
rc = -EINVAL;
goto exit;
}
/* Verify the reserved bits are 0 */
reserved_bits = pip2_cmd->read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0xF0;
if (reserved_bits)
pt_debug(cd->dev, DL_WARN,
"%s cmd[0x%02X] reserved_bits = 0x%02X\n",
__func__, pip2_cmd->cmd_id, reserved_bits);
exit:
if (rc)
pt_pr_buf(cd->dev, DL_WARN, cd->input_buf,
pip2_cmd->actual_read_len, "PIP RSP:");
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip1_validate_bl_response
*
* SUMMARY: Validate the response of bootloader command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_validate_bl_response(
struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
u16 size;
u16 crc;
u8 status;
size = get_unaligned_le16(&cd->response_buf[0]);
if (pip1_cmd->reset_expected && !size)
return 0;
if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
!= PT_PIP_BL_RESPONSE_REPORT_ID) {
pt_debug(cd->dev, DL_ERROR,
"%s: BL output response, wrong report_id\n", __func__);
return -EPROTO;
}
if (cd->response_buf[4] != PIP1_BL_SOP) {
pt_debug(cd->dev, DL_ERROR,
"%s: BL output response, wrong SOP\n", __func__);
return -EPROTO;
}
if (cd->response_buf[size - 1] != PIP1_BL_EOP) {
pt_debug(cd->dev, DL_ERROR,
"%s: BL output response, wrong EOP\n", __func__);
return -EPROTO;
}
crc = _pt_compute_crc(&cd->response_buf[4], size - 7);
if (cd->response_buf[size - 3] != LOW_BYTE(crc)
|| cd->response_buf[size - 2] != HI_BYTE(crc)) {
pt_debug(cd->dev, DL_ERROR,
"%s: BL output response, wrong CRC 0x%X\n",
__func__, crc);
return -EPROTO;
}
status = cd->response_buf[5];
if (status) {
pt_debug(cd->dev, DL_ERROR,
"%s: BL output response, ERROR:%d\n",
__func__, status);
return -EPROTO;
}
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip1_validate_app_response
*
* SUMMARY: Validate the response of application command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_validate_app_response(
struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
int command_code;
u16 size;
size = get_unaligned_le16(&cd->response_buf[0]);
/*
* This case is for reset sentinel, it could be one of those:
* APP sentinel: size is 0;
* BL sentinel of Gen5/6: size is 0;
* BL sentinel of TC3XXX/TT7XXX/DP818: tested by macro IS_BL_SENTINEL().
*/
if (pip1_cmd->reset_expected && (!size || IS_BL_SENTINEL(cd, size)))
return 0;
if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
!= PT_PIP_NON_HID_RESPONSE_ID) {
pt_debug(cd->dev, DL_ERROR,
"%s: APP output response, wrong report_id\n", __func__);
return -EPROTO;
}
command_code = cd->response_buf[PIP1_RESP_COMMAND_ID_OFFSET]
& PIP1_RESP_COMMAND_ID_MASK;
if (command_code != pip1_cmd->command_code) {
pt_debug(cd->dev, DL_ERROR,
"%s: APP output response, wrong command_code:%X\n",
__func__, command_code);
return -EPROTO;
}
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip1_check_set_parameter
*
* SUMMARY: Takes 'struct pt_pip1_cmd *' as an input parameter to check command
* input and response for Set Parameter command.And store the parameter to the
* list for resume work if pass the check.
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static void pt_pip1_check_set_parameter(struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
u8 *param_buf;
u32 param_value = 0;
u8 param_size;
u8 param_id;
u8 read_size;
u8 read_param_id;
int i = 0;
if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
return;
/* Check command input for Set Parameter command */
if (pip1_cmd->cmd_type == PIP1_CMD_TYPE_FW
&& pip1_cmd->command_code == PIP1_CMD_ID_SET_PARAM
&& pip1_cmd->write_length >= 3
&& pip1_cmd->write_length <= 6)
param_buf = &pip1_cmd->write_buf[0];
else
return;
/* Get parameter ID, size and value */
param_id = param_buf[0];
param_size = param_buf[1];
if (param_size > 4) {
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid parameter size\n", __func__);
return;
}
param_buf = &param_buf[2];
while (i < param_size)
param_value += *(param_buf++) << (8 * i++);
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
read_param_id = cd->response_buf[6];
read_size = cd->response_buf[7];
} else {
read_param_id = cd->response_buf[5];
read_size = cd->response_buf[6];
}
/* Check command response for Set Parameter command */
if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID
|| (cd->response_buf[4] &
PIP1_RESP_COMMAND_ID_MASK) !=
PIP1_CMD_ID_SET_PARAM
|| read_param_id != param_id
|| read_size != param_size) {
pt_debug(cd->dev, DL_ERROR,
"%s: Set Parameter command not successful\n",
__func__);
return;
}
pt_add_parameter(cd, param_id, param_value, param_size);
}
/*******************************************************************************
* FUNCTION: pt_raw_check_set_parameter
*
* SUMMARY: Takes 'struct pt_raw_cmd *' as an input parameter to check command
* input and response for Set Parameter command.And store the parameter to the
* list for resume work if pass the check.
*
* PARAMETERS:
* *cd - pointer to core data
* *raw_cmd - pointer to raw command data structure
******************************************************************************/
static void pt_raw_check_set_parameter(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
u8 *param_buf;
u32 param_value = 0;
u8 param_size;
u8 param_id;
u8 read_param_id;
u8 read_size;
int i = 0;
if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
return;
/* Check command input for Set Parameter command */
if (raw_cmd->write_length >= 10 && raw_cmd->write_length <= 13
&& !memcmp(&raw_cmd->write_buf[0],
&cd->hid_desc.output_register,
sizeof(cd->hid_desc.output_register))
&& raw_cmd->write_buf[4] ==
PT_PIP_NON_HID_COMMAND_ID
&& raw_cmd->write_buf[6] ==
PIP1_CMD_ID_SET_PARAM)
param_buf = &raw_cmd->write_buf[7];
else
return;
/* Get parameter ID, size and value */
param_id = param_buf[0];
param_size = param_buf[1];
if (param_size > 4) {
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid parameter size\n", __func__);
return;
}
param_buf = &param_buf[2];
while (i < param_size)
param_value += *(param_buf++) << (8 * i++);
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
read_param_id = cd->response_buf[6];
read_size = cd->response_buf[7];
} else {
read_param_id = cd->response_buf[5];
read_size = cd->response_buf[6];
}
/* Check command response for Set Parameter command */
if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID
|| (cd->response_buf[4] &
PIP1_RESP_COMMAND_ID_MASK) !=
PIP1_CMD_ID_SET_PARAM
|| read_param_id != param_id
|| read_size != param_size) {
pt_debug(cd->dev, DL_ERROR,
"%s: Set Parameter command not successful\n",
__func__);
return;
}
pt_add_parameter(cd, param_id, param_value, param_size);
}
/*******************************************************************************
* FUNCTION: pt_pip1_validate_response
*
* SUMMARY: Validate PIP1 response of application or bootloader command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_validate_response(struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
if (pip1_cmd->cmd_type == PIP1_CMD_TYPE_BL)
return pt_pip1_validate_bl_response(cd, pip1_cmd);
return pt_pip1_validate_app_response(cd, pip1_cmd);
}
/*******************************************************************************
* FUNCTION: pt_write_raw_direct_
*
* SUMMARY: Blindly send raw data to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *raw_cmd - pointer to raw command data structure
******************************************************************************/
static int pt_write_raw_direct_(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
int rc = 0;
int cmd;
if (!raw_cmd->write_length || !raw_cmd->write_buf)
return -EINVAL;
if (cd->pip2_prot_active) {
cmd = raw_cmd->write_buf[PIP2_CMD_COMMAND_ID_OFFSET];
cmd &= PIP2_CMD_COMMAND_ID_MASK;
} else
cmd = raw_cmd->write_buf[PIP1_CMD_COMMAND_ID_OFFSET];
pt_debug(cd->dev, DL_INFO,
">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
__func__, raw_cmd->write_length, cmd);
pt_pr_buf(cd->dev, DL_DEBUG, raw_cmd->write_buf,
raw_cmd->write_length, ">>> User CMD");
rc = pt_adap_write_read_specific(cd, raw_cmd->write_length,
raw_cmd->write_buf, NULL, 0);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Fail pt_adap_transfer\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_write_raw_direct
*
* SUMMARY: Wrapper function for pt_write_raw_direct_ that guarantees
* exclusive access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *raw_cmd - pointer to raw command data structure
******************************************************************************/
static int pt_write_raw_direct(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_write_raw_direct_(cd, raw_cmd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
#ifdef FUTURE
static int pt_raw_send_as_pip_(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
return 0;
}
static int pt_raw_send_as_hid_(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
return 0;
}
static int pt_raw_send_(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
return 0;
}
#endif
/*******************************************************************************
* FUNCTION: pt_raw_send_and_wait_
*
* SUMMARY: Blindly send raw data to the DUT and wait for the response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *raw_cmd - pointer to raw command data structure
******************************************************************************/
static int pt_raw_send_and_wait_(struct pt_core_data *cd,
struct pt_raw_cmd *raw_cmd)
{
int rc = 0;
int t;
u16 size = 0;
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = PIP1_CMD_ID_USER_CMD + 1;
mutex_unlock(&cd->system_lock);
rc = pt_write_raw_direct_(cd, raw_cmd);
if (rc)
goto error;
t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
msecs_to_jiffies(cd->pip_cmd_timeout));
if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR,
"%s: HID output cmd execution timed out\n",
__func__);
rc = -ETIME;
goto error;
}
/* Get the response size from the first 2 bytes in the response */
size = get_unaligned_le16(&cd->response_buf[0]);
/* Ensure size is not greater than max buffer size */
if (size > PT_MAX_PIP2_MSG_SIZE)
size = PT_MAX_PIP2_MSG_SIZE;
/* Minimum size to read is the 2 byte len field */
if (size == 0)
size = 2;
if (size > raw_cmd->read_len) {
pt_debug(cd->dev, DL_ERROR,
"%s: PIP2 len field=%d, requested read_len=%d\n",
__func__, size, raw_cmd->read_len);
raw_cmd->actual_read_len = 0;
rc = -EIO;
goto error;
}
if (raw_cmd->read_buf) {
raw_cmd->actual_read_len = size;
memcpy(raw_cmd->read_buf, cd->response_buf, size);
}
pt_raw_check_set_parameter(cd, raw_cmd);
goto exit;
error:
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = 0;
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_check_irq_asserted
*
* SUMMARY: Checks if the IRQ GPIO is asserted or not. There are times when
* the FW can hold the INT line low ~150us after the read is complete.
* NOTE: if irq_stat is not defined this function will return false
*
* RETURN:
* true = IRQ asserted
* false = IRQ not asserted
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static bool pt_check_irq_asserted(struct pt_core_data *cd)
{
#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP
/*
* Workaround for FW defect, CDT165308
* bl_launch app creates a glitch in IRQ line
*/
if (cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1
&& cd->cpdata->irq_stat) {
/*
* in X1S panel and GC1546 panel, the width for the INT
* glitch is about 4us,the normal INT width of response
* will last more than 200us, so use 10us delay
* for distinguish the glitch the normal INT is enough.
*/
udelay(10);
}
#endif
if (cd->cpdata->irq_stat) {
if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
== PT_IRQ_ASSERTED_VALUE) {
/* Debounce to allow FW to release INT */
usleep_range(100, 200);
}
if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
== PT_IRQ_ASSERTED_VALUE)
return true;
else
return false;
}
return true;
}
/*******************************************************************************
* FUNCTION: pt_flush_bus
*
* SUMMARY: Force flushing the bus by reading len bytes or forced 255 bytes
* Used if IRQ is found to be stuck low
*
* RETURN: Length of bytes read from bus
*
* PARAMETERS:
* *cd - pointer to core data
* flush_type - type of flush
* - PT_FLUSH_BUS_BASED_ON_LEN (two reads)
* - PT_FLUSH_BUS_FULL_256_READ
* *read_buf - pointer to store read data
******************************************************************************/
static ssize_t pt_flush_bus(struct pt_core_data *cd,
u8 flush_type, u8 *read_buf)
{
u8 buf[PT_MAX_PIP2_MSG_SIZE];
u16 pip_len;
int bytes_read;
int rc = 0;
if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) {
#ifdef TTDL_PTVIRTDUT_SUPPORT
if (cd->route_bus_virt_dut)
rc = pt_adap_read_default(cd, buf,
PT_MAX_PIP2_MSG_SIZE);
else
rc = pt_adap_read_default(cd, buf, 2);
#else
rc = pt_adap_read_default(cd, buf, 2);
#endif /* TTDL_PTVIRTDUT_SUPPORT */
if (rc) {
bytes_read = 0;
goto exit;
}
pip_len = get_unaligned_le16(&buf[0]);
if (pip_len == 2 || pip_len >= PT_PIP_1P7_EMPTY_BUF) {
#ifdef TTDL_DIAGNOSTICS
pt_toggle_err_gpio(cd, PT_ERR_GPIO_EMPTY_PACKET);
#endif
bytes_read = 2;
pt_debug(cd->dev, DL_INFO,
"%s: Empty buf detected - len=0x%04X\n",
__func__, pip_len);
} else if (pip_len == 0) {
bytes_read = 0;
pt_debug(cd->dev, DL_INFO,
"%s: Sentinel detected\n", __func__);
} else if (pip_len > PT_MAX_PIP2_MSG_SIZE) {
pt_debug(cd->dev, DL_ERROR,
"%s: Illegal len=0x%04x, force %d byte read\n",
__func__, pip_len, PT_MAX_PIP2_MSG_SIZE);
rc = pt_adap_read_default(cd, buf,
PT_MAX_PIP2_MSG_SIZE);
if (!rc)
bytes_read = PT_MAX_PIP2_MSG_SIZE;
else
bytes_read = 0;
} else {
pt_debug(cd->dev, DL_INFO,
"%s: Flush read of %d bytes...\n",
__func__, pip_len);
#ifdef TTDL_PTVIRTDUT_SUPPORT
rc = 0;
if (cd->route_bus_virt_dut)
bytes_read = pip_len;
else
rc = pt_adap_read_default(cd, buf, pip_len);
#else
rc = pt_adap_read_default(cd, buf, pip_len);
#endif /* TTDL_PTVIRTDUT_SUPPORT */
if (!rc)
bytes_read = pip_len;
else
bytes_read = 0;
}
} else {
pt_debug(cd->dev, DL_INFO,
"%s: Forced flush of max %d bytes...\n",
__func__, PT_MAX_PIP2_MSG_SIZE);
rc = pt_adap_read_default(cd, buf, PT_MAX_PIP2_MSG_SIZE);
if (!rc)
bytes_read = PT_MAX_PIP2_MSG_SIZE;
else
bytes_read = 0;
}
if (bytes_read > 0)
pt_pr_buf(cd->dev, DL_WARN, buf, bytes_read, "WD-flush_bus");
if (read_buf && (bytes_read > 3))
memcpy(read_buf, buf, bytes_read);
exit:
return bytes_read;
}
/*******************************************************************************
* FUNCTION: pt_flush_bus_if_irq_asserted
*
* SUMMARY: This function will flush the active bus if the INT is found to be
* asserted.
*
* RETURN: bytes cleared from bus
*
* PARAMETERS:
* *cd - pointer the core data structure
* flush_type - type of flush
* - PT_FLUSH_BUS_BASED_ON_LEN
* - PT_FLUSH_BUS_FULL_256_READ
******************************************************************************/
static int pt_flush_bus_if_irq_asserted(struct pt_core_data *cd, u8 flush_type)
{
int count = 0;
int bytes_read = 0;
while (pt_check_irq_asserted(cd) && count < 5) {
count++;
bytes_read = pt_flush_bus(cd, flush_type, NULL);
if (bytes_read) {
pt_debug(cd->dev, DL_WARN,
"%s: Cleared %d bytes off bus\n",
__func__, bytes_read);
}
}
if (pt_check_irq_asserted(cd)) {
pt_debug(cd->dev, DL_ERROR,
"%s: IRQ still asserted, %d bytes read\n",
__func__, bytes_read);
} else {
pt_debug(cd->dev, DL_INFO,
"%s: IRQ cleared, %d bytes read\n",
__func__, bytes_read);
}
return bytes_read;
}
/*******************************************************************************
* FUNCTION: pt_write_to_hid_output_reg_
*
* SUMMARY: Low level HID function to send touch command to the DUT's output
* register under HID protocol.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hid_output - pointer to the command to write to output register
******************************************************************************/
static int pt_write_to_hid_output_reg_(struct pt_core_data *cd,
struct pt_hid_output *hid_output)
{
int rc = 0;
u8 *cmd;
u16 length;
u16 crc;
u8 report_id;
u8 cmd_offset = 0;
u8 cmd_allocated = 0;
#ifdef FUTURE
/*
* *** TODO - Determine side effects of adding this safty net ***
* If IRQ is already asserted due to a pending report, it must be
* cleared before sending command.
*/
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
#endif
switch (hid_output->cmd_type) {
case PIP1_CMD_TYPE_FW:
report_id = PT_PIP_NON_HID_COMMAND_ID;
length = 5;
break;
case PIP1_CMD_TYPE_BL:
report_id = PT_PIP_BL_COMMAND_REPORT_ID;
length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */;
break;
default:
return -EINVAL;
}
length += hid_output->write_length;
if (length + 2 > PT_PREALLOCATED_CMD_BUFFER) {
cmd = kzalloc(length + 2, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd_allocated = 1;
} else {
cmd = cd->cmd_buf;
}
/* Set Output register */
memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register,
sizeof(cd->hid_desc.output_register));
cmd_offset += sizeof(cd->hid_desc.output_register);
cmd[cmd_offset++] = LOW_BYTE(length);
cmd[cmd_offset++] = HI_BYTE(length);
cmd[cmd_offset++] = report_id;
cmd[cmd_offset++] = 0x0; /* reserved */
if (hid_output->cmd_type == PIP1_CMD_TYPE_BL)
cmd[cmd_offset++] = PIP1_BL_SOP;
cmd[cmd_offset++] = hid_output->command_code;
/* Set Data Length for bootloader */
if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length);
cmd[cmd_offset++] = HI_BYTE(hid_output->write_length);
}
/* Set Data */
if (hid_output->write_length && hid_output->write_buf) {
memcpy(&cmd[cmd_offset], hid_output->write_buf,
hid_output->write_length);
cmd_offset += hid_output->write_length;
}
if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
crc = _pt_compute_crc(&cmd[6],
hid_output->write_length + 4);
cmd[cmd_offset++] = LOW_BYTE(crc);
cmd[cmd_offset++] = HI_BYTE(crc);
cmd[cmd_offset++] = PIP1_BL_EOP;
}
pt_debug(cd->dev, DL_DEBUG, ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", __func__,
length + 2, hid_output->command_code);
pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD");
rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL, 0);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Fail pt_adap_transfer rc=%d\n", __func__, rc);
if (cmd_allocated)
kfree(cmd);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip1_send_as_pip_
*
* SUMMARY: Wrapper function to send PIP1 command to HID output register
* register with function pt_write_to_hid_output_reg_().
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_send_as_pip_(struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
struct pt_hid_output hid_output = {
INIT_HID_OUTPUT_FROM_PIP_CMD(pip1_cmd),
};
return pt_write_to_hid_output_reg_(cd, &hid_output);
}
/*******************************************************************************
* FUNCTION: pt_pip1_send_as_hid_
*
* SUMMARY: Function to pack and send PIP1 command to DUT's HID command
* register with HID SET_REPORT command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_send_as_hid_(struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
struct pt_hid_cmd hid_cmd = {
INIT_HID_SET_REPORT_HEADER,
};
int rc = 0;
u16 index = 0;
u16 payload_len = 0;
unsigned short crc;
/*
* 'write_length' is calculated by:
* Length field(2) +
* REPORT_ID(1) +
* Payload (hid_max_output_len)
*/
hid_cmd.write_length = 3 + cd->hid_core.hid_max_output_len;
if (hid_cmd.write_length < (9 + pip1_cmd->write_length))
return -EINVAL;
hid_cmd.write_buf = kzalloc(hid_cmd.write_length, GFP_KERNEL);
if (!hid_cmd.write_buf)
return -ENOMEM;
/* Set Length Field(2) */
hid_cmd.write_buf[index++] = LOW_BYTE(hid_cmd.write_length);
hid_cmd.write_buf[index++] = HI_BYTE(hid_cmd.write_length);
/* Set HID output Report ID(1) */
hid_cmd.write_buf[index++] = PT_HID_OUTPUT_REPORT_ID;
/*
* 'payload_len' is calculated by:
* Length of payload(2) +
* TAG/SEQ(1) +
* command ID(1) +
* Parameter Data(pip1_cmd->write_length) +
* CRC(2)
*/
payload_len = 6 + pip1_cmd->write_length;
hid_cmd.write_buf[index++] = LOW_BYTE(payload_len);
hid_cmd.write_buf[index++] = HI_BYTE(payload_len);
/* Request and set SEQ(1) */
hid_cmd.write_buf[index++] = pt_pip2_get_next_cmd_seq(cd);
/* Set command code(1) */
hid_cmd.write_buf[index++] = pt_get_hid_cmd_id_from_pip(
pip1_cmd->command_code, PIP1_TO_HID_CMD_ID_MAP);
/* Copy PIP1 Parameter Data(N) */
memcpy(&hid_cmd.write_buf[index], pip1_cmd->write_buf,
pip1_cmd->write_length);
index += pip1_cmd->write_length;
/*
* Calculate CRC(2): from 'Length of payload' to 'PIP1 Parameter Data'
*/
crc = crc_ccitt_calculate(&hid_cmd.write_buf[3], index - 3);
hid_cmd.write_buf[index++] = (crc & 0xff00) >> 8;
hid_cmd.write_buf[index++] = (crc & 0xff);
rc = pt_write_to_hid_cmd_reg_(cd, &hid_cmd);
kfree(hid_cmd.write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip1_send_
*
* SUMMARY: Wrapper function to send PIP1 command to HID output register or
* HID command register (with HID SET_REPORT command) by function
* pt_pip1_send_as_pip_() and pt_pip1_send_as_hid_().
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_send_(struct pt_core_data *cd, struct pt_pip1_cmd *pip1_cmd)
{
cd->report_type = PIP1_CMD_REPORT;
if ((cd->protocol_mode == PT_PROTOCOL_MODE_HID) &&
(cd->mode != PT_MODE_BOOTLOADER))
return pt_pip1_send_as_hid_(cd, pip1_cmd);
else
return pt_pip1_send_as_pip_(cd, pip1_cmd);
}
/*******************************************************************************
* FUNCTION: pt_send_host_mode_cmd_
*
* SUMMARY: Sending the special "Host Mode" command will instruct the BL
* to not execute the FW it has loaded into RAM. The command must be sent
* within a 40ms window after chip reset by XRES pin or command. If the
* messages is sent too early it will NAK, so keep sending it every 2ms
* until it is accepted by the BL. A no-flash DUT does not require this
* command as there is no FW for the BL to load and execute.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static inline int pt_send_host_mode_cmd_(struct pt_core_data *cd)
{
int rc = 0;
u8 host_mode_cmd[4] = {0xA5, 0xA5, 0xA5, 0xA5};
u8 time = 0;
/* Wait BL to be ready to accept host command */
usleep_range(4000, 6000);
pt_debug(cd->dev, DL_INFO,
">>> %s: Write Buffer Size[%d] Stay in BL\n",
__func__, (int)sizeof(host_mode_cmd));
pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd,
(int)sizeof(host_mode_cmd), ">>> User CMD");
rc = 1;
while (rc && time < 34) {
rc = pt_adap_write_read_specific(cd, 4,
host_mode_cmd, NULL, 0);
usleep_range(1800, 2000);
time += 2;
}
/* Sleep to allow the BL to come up */
usleep_range(1000, 2000);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip1_send_and_wait_
*
* SUMMARY: Send valid PIP1 command to the DUT and wait for the response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP1 command data structure
******************************************************************************/
static int pt_pip1_send_and_wait_(struct pt_core_data *cd,
struct pt_pip1_cmd *pip1_cmd)
{
int rc = 0;
int t;
u16 timeout_ms;
u16 max_timeout_ms = PT_HID_MULTIPACKET_TIMEOUT;
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = pip1_cmd->command_code + 1;
mutex_unlock(&cd->system_lock);
if (pip1_cmd->timeout_ms)
timeout_ms = pip1_cmd->timeout_ms;
else
timeout_ms = cd->pip_cmd_timeout_default;
rc = pt_pip1_send_(cd, pip1_cmd);
if (rc)
goto error;
/*
* Enter bootloader by StartBootloader command for TC3XXX/TT7XXX/DP818
* 1) Send Startbootloader command
* 2) Send host_cmd within 40 ms
* 3) Receive BL sentinel
*/
if (pip1_cmd->command_code == PIP1_CMD_ID_START_BOOTLOADER &&
cd->active_dut_generation != DUT_PIP1_ONLY) {
pt_send_host_mode_cmd_(cd);
}
if (pip1_cmd->command_code == PIP1_BL_CMD_ID_LAUNCH_APP &&
cd->dual_mcu_available) {
pt_debug(cd->dev, DL_INFO,
"%s: Dual MCU is enabled, no sentinel after cmd 0x3B\n",
__func__);
/*
* Typically a Gen6 needs about 100ms to generate a sentinel on
* boot, however for a dual MCU solution no sentinel is produced
* but the wait time is still needed to allow the Gen6 to fully
* boot. The sleep time is doubled as a safe guard.
*/
msleep(200);
cd->hid_reset_cmd_state = 0;
goto exit;
}
again:
t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
msecs_to_jiffies(timeout_ms));
if (IS_TMO(t)) {
if ((cd->protocol_mode == PT_PROTOCOL_MODE_HID) &&
(cd->pt_hid_buf_op.last_remain_packet != 0) &&
(max_timeout_ms > timeout_ms)) {
max_timeout_ms -= timeout_ms;
goto again;
}
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR,
"%s: HID output cmd execution timed out (%dms)\n",
__func__, timeout_ms);
rc = -ETIME;
goto error;
}
if (!pip1_cmd->novalidate &&
cd->protocol_mode != PT_PROTOCOL_MODE_HID)
rc = pt_pip1_validate_response(cd, pip1_cmd);
pt_pip1_check_set_parameter(cd, pip1_cmd);
goto exit;
error:
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = 0;
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_write_to_pip2_cmd_reg_
*
* SUMMARY: Low level function to send PIP2 command to PIP2 command register.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip2_cmd - pointer to PIP2 command data structure
******************************************************************************/
static int pt_write_to_pip2_cmd_reg_(struct pt_core_data *cd,
struct pt_pip2_cmd *pip2_cmd)
{
int rc = 0;
int index = 0;
u16 crc = 0;
u16 cmd_len = 0;
u16 write_len = 0;
u8 *write_buf = NULL;
/* The PIP2.1+ the length field includes:
* 2 byte length
* 1 byte sequence
* 1 byte command ID
* N byte report body
* 2 byte CRC
*/
cmd_len = pip2_cmd->report_len + 6;
/* The overall write length include register(2 bytes) */
write_len = 2 + cmd_len;
write_buf = kzalloc(write_len, GFP_KERNEL);
if (write_buf == NULL) {
rc = -ENOMEM;
return rc;
}
/* PIP2 command register */
write_buf[index++] = LOW_BYTE(PT_PIP2_CMD_REGISTER);
write_buf[index++] = HI_BYTE(PT_PIP2_CMD_REGISTER);
/* Length field (LSB) */
write_buf[index++] = LOW_BYTE(cmd_len);
write_buf[index++] = HI_BYTE(cmd_len);
/* Tag and Seq, request it at first */
pip2_cmd->cmd_tag_seq = pt_pip2_get_next_cmd_seq(cd);
write_buf[index++] = pip2_cmd->cmd_tag_seq;
/* Command ID */
write_buf[index++] = pip2_cmd->cmd_id;
/* Report Body */
memcpy(&write_buf[index], pip2_cmd->report_data, pip2_cmd->report_len);
index += pip2_cmd->report_len;
/*
* The CRC calculation content includes:
* 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + N (report)
*/
crc = crc_ccitt_calculate(&write_buf[2], cmd_len - 2);
/* CRC (MSB) */
write_buf[index++] = HI_BYTE(crc);
write_buf[index++] = LOW_BYTE(crc);
pt_debug(cd->dev, DL_INFO,
">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
__func__, write_len, pip2_cmd->cmd_id);
pt_pr_buf(cd->dev, DL_DEBUG, write_buf, write_len, ">>> CMD");
rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL, 0);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Fail pt_adap_transfer\n", __func__);
kfree(write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_send_as_pip_
*
* SUMMARY: Wrapper function to send PIP2 command to DUT's PIP2 command
* register with function pt_write_to_pip2_cmd_reg_().
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip2_cmd - pointer to PIP2 command data structure
******************************************************************************/
static int pt_pip2_send_as_pip_(struct pt_core_data *cd,
struct pt_pip2_cmd *pip2_cmd)
{
return pt_write_to_pip2_cmd_reg_(cd, pip2_cmd);
}
/*******************************************************************************
* FUNCTION: pt_pip2_send_as_hid_
*
* SUMMARY: Function to pack and send PIP2 command to DUT's HID command
* register with HID SET_REPORT command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip2_cmd - pointer to PIP2 command data structure
******************************************************************************/
static int pt_pip2_send_as_hid_(struct pt_core_data *cd,
struct pt_pip2_cmd *pip2_cmd)
{
struct pt_hid_cmd hid_cmd = {
INIT_HID_SET_REPORT_HEADER,
};
int rc = 0;
u16 index = 0;
u16 payload_len = 0;
unsigned short crc;
/*
* 'write_length' is calculated by:
* Length field(2) +
* REPORT_ID(1) +
* Payload (hid_max_output_len)
*/
hid_cmd.write_length = 3 + cd->hid_core.hid_max_output_len;
if (hid_cmd.write_length < (pip2_cmd->report_len + 9))
return -EINVAL;
hid_cmd.write_buf = kzalloc(hid_cmd.write_length, GFP_KERNEL);
if (!hid_cmd.write_buf)
return -ENOMEM;
/* Set Length Field(2) */
hid_cmd.write_buf[index++] = LOW_BYTE(hid_cmd.write_length);
hid_cmd.write_buf[index++] = HI_BYTE(hid_cmd.write_length);
/* Set HID output Report ID(1) */
hid_cmd.write_buf[index++] = PT_HID_OUTPUT_REPORT_ID;
/*
* 'payload_len' is calculated by:
* Length of payload(2) +
* TAG/SEQ(1) +
* command ID(1) +
* report body(pip2_cmd->report_len) +
* CRC(2)
*/
payload_len = 6 + pip2_cmd->report_len;
hid_cmd.write_buf[index++] = LOW_BYTE(payload_len);
hid_cmd.write_buf[index++] = HI_BYTE(payload_len);
/* Request and set SEQ(1) */
pip2_cmd->cmd_tag_seq = pt_pip2_get_next_cmd_seq(cd);
hid_cmd.write_buf[index++] = pip2_cmd->cmd_tag_seq;
/* Set command ID(1) */
hid_cmd.write_buf[index++] = pt_get_hid_cmd_id_from_pip(
pip2_cmd->cmd_id, PIP2_TO_HID_CMD_ID_MAP);
/* Copy PIP2 report body(N) */
memcpy(&hid_cmd.write_buf[index], pip2_cmd->report_data,
pip2_cmd->report_len);
index += pip2_cmd->report_len;
/*
* Calcualte CRC(2): from 'Length of payload' to 'PIP2 report body'
*/
crc = crc_ccitt_calculate(&hid_cmd.write_buf[3], index - 3);
hid_cmd.write_buf[index++] = (crc & 0xff00) >> 8;
hid_cmd.write_buf[index++] = (crc & 0xff);
rc = pt_write_to_hid_cmd_reg_(cd, &hid_cmd);
kfree(hid_cmd.write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_send_
*
* SUMMARY: Wrapper function to send PIP2 command to PIP2 command register or
* HID command register (with HID SET_REPORT command) by function
* pt_pip2_send_as_pip_() and pt_pip2_send_as_hid_().
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip1_cmd - pointer to PIP2 command data structure
******************************************************************************/
static int pt_pip2_send_(struct pt_core_data *cd, struct pt_pip2_cmd *pip2_cmd)
{
cd->report_type = PIP2_CMD_REPORT;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(cd->mode != PT_MODE_BOOTLOADER))
return pt_pip2_send_as_hid_(cd, pip2_cmd);
else
return pt_pip2_send_as_pip_(cd, pip2_cmd);
}
/*******************************************************************************
* FUNCTION: pt_pip2_send_and_wait_
*
* SUMMARY: Send valid PIP2 command to the DUT and wait for the response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *pip2_cmd - pointer to PIP2 command data structure
******************************************************************************/
static int pt_pip2_send_and_wait_(struct pt_core_data *cd,
struct pt_pip2_cmd *pip2_cmd)
{
int rc = 0;
int t;
u16 size;
u16 read_len;
u16 timeout_ms;
u16 max_timeout_ms = PT_HID_MULTIPACKET_TIMEOUT;
/* PIP2 the cmd ID is a 7bit field */
if (pip2_cmd->cmd_id > PIP2_CMD_ID_END) {
pt_debug(cd->dev, DL_WARN, "%s: Invalid PIP2 CMD ID 0x%02X\n",
__func__, pip2_cmd->cmd_id);
rc = -EINVAL;
goto exit;
}
read_len = pt_pip2_get_cmd_response_len(pip2_cmd->cmd_id);
if (read_len < 0)
read_len = 255;
pt_debug(cd->dev, DL_INFO,
"%s cmd_id[0x%02X] expected response length:%d\n",
__func__, pip2_cmd->cmd_id, read_len);
mutex_lock(&cd->system_lock);
cd->pip2_prot_active = true;
cd->hid_cmd_state = pip2_cmd->cmd_id + 1;
mutex_unlock(&cd->system_lock);
if (pip2_cmd->timeout_ms)
timeout_ms = pip2_cmd->timeout_ms;
else
timeout_ms = cd->pip_cmd_timeout_default;
rc = pt_pip2_send_(cd, pip2_cmd);
if (rc)
goto error;
again:
t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
msecs_to_jiffies(timeout_ms));
if (IS_TMO(t)) {
if ((cd->protocol_mode == PT_PROTOCOL_MODE_HID) &&
(cd->pt_hid_buf_op.last_remain_packet != 0) &&
(max_timeout_ms > timeout_ms)) {
max_timeout_ms -= timeout_ms;
goto again;
}
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR,
"%s: PIP2 cmd execution timed out (%dms)\n",
__func__, timeout_ms);
rc = -ETIME;
goto error;
}
/* Get the response size from the first 2 bytes in the response */
size = get_unaligned_le16(&cd->response_buf[0]);
/* Ensure size is not greater than max buffer size */
if (size > PT_MAX_PIP2_MSG_SIZE)
size = PT_MAX_PIP2_MSG_SIZE;
/* Minimum size to read is the 2 byte len field */
if (size == 0)
size = 2;
if (size > read_len) {
pt_debug(cd->dev, DL_ERROR,
"%s: PIP2 len field=%d, requested read_len=%d\n",
__func__, size, read_len);
pip2_cmd->actual_read_len = 0;
rc = -EIO;
goto error;
}
if (pip2_cmd->read_buf) {
pip2_cmd->actual_read_len = size;
memcpy(pip2_cmd->read_buf, cd->response_buf, size);
}
if (!pip2_cmd->novalidate)
rc = pt_pip2_validate_response(cd, pip2_cmd);
error:
mutex_lock(&cd->system_lock);
cd->pip2_prot_active = false;
cd->hid_cmd_state = 0;
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_user_cmd_
*
* SUMMARY: Load the write buffer into a HID structure and send it as a HID cmd
* to the DUT waiting for the response and loading it into the read buffer
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_len - expected read length of the response
* *read_buf - pointer to where the response will be loaded
* write_len - length of the write buffer
* *write_buf - pointer to the write buffer
* *actual_read_len - pointer to the actual amount of data read back
******************************************************************************/
static int pt_hid_output_user_cmd_(struct pt_core_data *cd,
u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
u16 *actual_read_len)
{
int rc = 0;
struct pt_raw_cmd raw_cmd = {
.write_length = write_len,
.write_buf = write_buf,
.read_buf = read_buf,
.read_len = read_len
};
#ifdef TTHE_TUNER_SUPPORT
if (!cd->pip2_send_user_cmd) {
int command_code = 0;
int len;
/* Print up to cmd ID */
len = PIP1_CMD_COMMAND_ID_OFFSET + 1;
if (write_len < len)
len = write_len;
else
command_code = write_buf[PIP1_CMD_COMMAND_ID_OFFSET]
& PIP1_CMD_COMMAND_ID_MASK;
/* Don't print EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */
if (command_code != PIP1_CMD_ID_EXEC_PANEL_SCAN &&
command_code != PIP1_CMD_ID_RETRIEVE_PANEL_SCAN)
tthe_print(cd, write_buf, len, "CMD=");
}
#endif
rc = pt_raw_send_and_wait_(cd, &raw_cmd);
if (rc)
return rc;
if (actual_read_len)
*actual_read_len = raw_cmd.actual_read_len;
return 0;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_user_cmd
*
* SUMMARY: Protected call to pt_hid_output_user_cmd_ by exclusive access to
* the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_len - length of data to read
* *read_buf - pointer to store read data
* write_len - length of data to write
* *write_buf - pointer to buffer to write
* *actual_read_len - pointer to store data length actually read
******************************************************************************/
static int pt_hid_output_user_cmd(struct pt_core_data *cd,
u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
u16 *actual_read_len)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_user_cmd_(cd, read_len, read_buf,
write_len, write_buf, actual_read_len);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip2_send_cmd
*
* SUMMARY: Writes a PIP2 command packet to DUT, then waits for the
* interrupt and reads response data to read_buf
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to run in protected mode
* id - ID of PIP command
* *data - pointer to PIP data payload
* report_body_len - report length
* *read_buf - pointer to response buffer
* *actual_read_len - pointer to response buffer length
******************************************************************************/
static int _pt_request_pip2_send_cmd(struct device *dev,
int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
u16 *actual_read_len)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pip2_cmd pip2_cmd = {
.cmd_id = id,
.report_data = data,
.report_len = report_body_len,
.read_buf = read_buf,
};
int rc;
if (protect) {
rc = request_exclusive(cd, cd->dev,
PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
}
rc = pt_pip2_send_and_wait_(cd, &pip2_cmd);
if (rc)
goto exit;
if (actual_read_len)
*actual_read_len = pip2_cmd.actual_read_len;
exit:
if (protect) {
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
}
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_send_cmd_no_int
*
* SUMMARY: Writes a PIP2 command packet to DUT, then poll the response and
* reads response data to read_buf if response is available.
*
* NOTE:
* Interrupt MUST be disabled before to call this function.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to run in protected mode
* id - ID of PIP command
* *data - pointer to PIP data payload
* report_body_len - report length
* *read_buf - pointer to response buffer
* *actual_read_len - pointer to response buffer length
******************************************************************************/
static int _pt_pip2_send_cmd_no_int(struct device *dev,
int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
u16 *actual_read_len)
{
int max_retry = 0;
int retry = 0;
int rc = 0;
int read_len;
u16 size = 0;
u8 response_seq = 0;
u32 retry_interval = 0;
u32 retry_total_time = 0;
u32 resp_time_min = pt_pip2_get_cmd_resp_time_min(id);
u32 resp_time_max = pt_pip2_get_cmd_resp_time_max(id);
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pip2_cmd pip2_cmd = {
.cmd_id = id,
.report_data = data,
.report_len = report_body_len
};
if (protect == PT_CORE_CMD_PROTECTED) {
rc = request_exclusive(cd,
cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
}
read_len = pt_pip2_get_cmd_response_len(pip2_cmd.cmd_id);
if (read_len < 0)
read_len = 255;
pt_debug(dev, DL_INFO,
"%s: ATM - cmd_id[0x%02X] expected response length:%d\n",
__func__, pip2_cmd.cmd_id, read_len);
rc = pt_pip2_send_as_pip_(cd, &pip2_cmd);
if (rc) {
pt_debug(dev, DL_ERROR, "%s: PIP2 command send err=%d\n",
__func__, rc);
goto exit;
}
#ifdef PT_POLL_RESP_BY_BUS
/*
* Frequent bus read can increase system load obviously. The expected
* first bus read should be valid and timely. The tollerance for
* usleep_range should be limited. The minimum response delay (between
* command finishes sending and INT pin falls) is less than 50
* microseconds. So the 10 microseconds should be maximum tollerance
* with the consideration that the unit to calculate the response delay
* is 10 microseconds and more precise is not necessary. Every
* additional 10 microseconds only contribute less than 3 milliseconds
* for whole BL.
*/
usleep_range(resp_time_min, resp_time_min+10);
max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
rc = pt_adap_read_default(cd, read_buf, read_len);
if (rc) {
pt_debug(dev, DL_ERROR, "%s: SPI read Error = %d\n",
__func__, rc);
break;
}
response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET];
size = get_unaligned_le16(&read_buf[0]);
if ((size <= read_len) &&
(size >= PIP2_EXTRA_BYTES_NUM) &&
(pip2_cmd.seq & 0x07) == (response_seq & 0x07)) {
break;
}
/*
* To reduce the bus and system load, increase the sleep
* step gradually:
* 1 ~ 19 : step=50 us, sleep_us=[50, 100, 150, 200, ..950]
* 20 ~ 39 : step=1000 us, sleep_us=[1950, 2950, ...20950]
* 40 ~ MAX: step=50 ms, sleep_ms=[71, 121, 191,..]
*/
retry++;
if (retry < 20) {
retry_interval += POLL_RETRY_DEFAULT_INTERVAL;
usleep_range(retry_interval,
retry_interval + POLL_RETRY_DEFAULT_INTERVAL);
} else if (retry < 40) {
retry_interval += 1000;
usleep_range(retry_interval, retry_interval + 1000);
} else {
retry_interval += 50000;
msleep(retry_interval/1000);
}
retry_total_time += retry_interval;
}
#else
/*
* Frequent GPIO read will not increase CPU/sytem load heavily if the
* interval is longer than 10 us, so it is safe to poll GPIO with a
* fixed interval: 20 us.
*/
usleep_range(resp_time_min, resp_time_min+10);
max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
if (!gpio_get_value(cd->cpdata->irq_gpio)) {
rc = pt_adap_read_default(cd, read_buf, read_len);
response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET];
size = get_unaligned_le16(&read_buf[0]);
if (rc)
pt_debug(dev, DL_ERROR,
"%s: SPI read Error = %d\n",
__func__, rc);
else if (size > read_len) {
pt_debug(cd->dev, DL_ERROR,
"%s: PIP2 len field=%d, requested read_len=%d\n",
__func__, size, read_len);
rc = -EIO;
}
break;
}
/*
* Poll GPIO with fixed interval 20 us, and tollerance is
* limited to 10 us to speed up the process.
*/
retry_interval = POLL_RETRY_DEFAULT_INTERVAL;
usleep_range(retry_interval, retry_interval+10);
retry_total_time += retry_interval;
}
#endif
*actual_read_len = size;
if (rc || (retry >= max_retry) || (retry_total_time >= resp_time_max)) {
pt_debug(dev, DL_ERROR,
"%s cmd[0x%02X] timed out, send_seq=0x%02X, resp_seq=0x%02X\n",
__func__, pip2_cmd.cmd_id, pip2_cmd.cmd_tag_seq,
response_seq);
*actual_read_len = 0;
rc = -EINVAL;
}
pt_pr_buf(cd->dev, DL_DEBUG, read_buf, *actual_read_len,
"<<< NO_INT Read");
exit:
if (protect == PT_CORE_CMD_PROTECTED) {
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_null_
*
* SUMMARY: Send the PIP "ping"(0x00) command to the DUT and wait for response.
* This function is used by watchdog to check if the fw corrupts.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_pip_null_(struct pt_core_data *cd)
{
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL),
};
return pt_pip1_send_and_wait_(cd, &pip1_cmd);
}
#ifndef TTDL_KERNEL_SUBMISSION
/*******************************************************************************
* FUNCTION: pt_pip_null
*
* SUMMARY: Wrapper function for pt_pip_null_ that guarantees exclusive access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_pip_null(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_null_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
#endif /*!TTDL_KERNEL_SUBMISSION */
static void pt_stop_wd_timer(struct pt_core_data *cd);
/*******************************************************************************
* FUNCTION: pt_pip_start_bootloader_
*
* SUMMARY: Sends the PIP command start_bootloader [PIP cmd 0x01] to the DUT
*
* NOTE: The WD MUST be stopped/restarted by the calling Function. Having
* the WD active could cause this function to fail!
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_pip_start_bootloader_(struct pt_core_data *cd)
{
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_START_BOOTLOADER),
.timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT,
.reset_expected = 1,
};
if (cd->watchdog_enabled) {
pt_debug(cd->dev, DL_WARN,
"%s: watchdog isn't stopped before enter bl\n",
__func__);
goto exit;
}
/* Reset enum status after entering BL, new DUT enum required */
cd->enum_status = ENUM_STATUS_START;
pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Start BL PIP cmd failed. rc = %d\n",
__func__, rc);
}
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_start_bootloader
*
* SUMMARY: Protected function to force DUT to enter the BL
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip_start_bootloader(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_start_bootloader_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_start_bl
*
* SUMMARY: Function pointer included in core_nonhid_cmds to allow other
* modules to request the DUT to enter the BL
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to run in protected mode
******************************************************************************/
static int _pt_request_pip_start_bl(struct device *dev, int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_start_bootloader(cd);
return pt_pip_start_bootloader_(cd);
}
/*******************************************************************************
* FUNCTION: pt_pip2_ver_load_ttdata
*
* SUMMARY: Function to load the Version information from the PIP2 VERSION
* command into the core data struct.
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data structure
* len - Length of data in response_buf
******************************************************************************/
static void pt_pip2_ver_load_ttdata(struct pt_core_data *cd, u16 len)
{
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
struct pt_pip2_version_full *full_ver;
struct pt_pip2_version *ver;
/*
* The PIP2 VERSION command can return different lengths of data.
* The additional LOT fields are included when the packet
* size is >= 29 bytes. Older FW sends a reduced packet size.
* NOTE:
* - The FW would swap the BL and FW versions when reporting
* the small packet.
* - Sub Lot bytes 16 and 17 are reserved.
*/
if (len >= 0x1D) {
full_ver = (struct pt_pip2_version_full *)
&cd->response_buf[PIP2_RESP_STATUS_OFFSET];
ttdata->pip_ver_major = full_ver->pip2_version_msb;
ttdata->pip_ver_minor = full_ver->pip2_version_lsb;
ttdata->bl_ver_major = full_ver->bl_version_msb;
ttdata->bl_ver_minor = full_ver->bl_version_lsb;
ttdata->fw_ver_major = full_ver->fw_version_msb;
ttdata->fw_ver_minor = full_ver->fw_version_lsb;
/*
* BL PIP 2.02 and greater the version fields are
* swapped
*/
if (ttdata->pip_ver_major >= 2 && ttdata->pip_ver_minor >= 2) {
ttdata->chip_rev =
get_unaligned_le16(&full_ver->chip_rev);
ttdata->chip_id =
get_unaligned_le16(&full_ver->chip_id);
} else {
ttdata->chip_rev =
get_unaligned_le16(&full_ver->chip_id);
ttdata->chip_id =
get_unaligned_le16(&full_ver->chip_rev);
}
memcpy(ttdata->uid, full_ver->uid, PT_UID_SIZE);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)full_ver,
sizeof(struct pt_pip2_version_full),
"PIP2 VERSION FULL");
} else {
ver = (struct pt_pip2_version *)
&cd->response_buf[PIP2_RESP_STATUS_OFFSET];
ttdata->pip_ver_major = ver->pip2_version_msb;
ttdata->pip_ver_minor = ver->pip2_version_lsb;
ttdata->bl_ver_major = ver->bl_version_msb;
ttdata->bl_ver_minor = ver->bl_version_lsb;
ttdata->fw_ver_major = ver->fw_version_msb;
ttdata->fw_ver_minor = ver->fw_version_lsb;
ttdata->chip_rev = get_unaligned_le16(&ver->chip_rev);
ttdata->chip_id = get_unaligned_le16(&ver->chip_id);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)ver,
sizeof(struct pt_pip2_version), "PIP2 VERSION");
}
}
/*******************************************************************************
* FUNCTION: pt_si_get_ttdata
*
* SUMMARY: Function to load the version information from the system information
* PIP command into the core data struct.
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_si_get_ttdata(struct pt_core_data *cd)
{
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
struct pt_ttdata_dev *ttdata_dev;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID)
ttdata_dev =
(struct pt_ttdata_dev *)
&cd->response_buf[HID_SYSINFO_TTDATA_OFFSET];
else
ttdata_dev =
(struct pt_ttdata_dev *)
&cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];
ttdata->pip_ver_major = ttdata_dev->pip_ver_major;
ttdata->pip_ver_minor = ttdata_dev->pip_ver_minor;
ttdata->bl_ver_major = ttdata_dev->bl_ver_major;
ttdata->bl_ver_minor = ttdata_dev->bl_ver_minor;
ttdata->fw_ver_major = ttdata_dev->fw_ver_major;
ttdata->fw_ver_minor = ttdata_dev->fw_ver_minor;
ttdata->fw_pid = get_unaligned_le16(&ttdata_dev->fw_pid);
ttdata->fw_ver_conf = get_unaligned_le16(&ttdata_dev->fw_ver_conf);
ttdata->post_code = get_unaligned_le16(&ttdata_dev->post_code);
ttdata->revctrl = get_unaligned_le32(&ttdata_dev->revctrl);
ttdata->jtag_id_l = get_unaligned_le16(&ttdata_dev->jtag_si_id_l);
ttdata->jtag_id_h = get_unaligned_le16(&ttdata_dev->jtag_si_id_h);
memcpy(ttdata->mfg_id, ttdata_dev->mfg_id, PT_NUM_MFGID);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)ttdata_dev,
sizeof(struct pt_ttdata_dev), "sysinfo_ttdata");
}
/*******************************************************************************
* FUNCTION: pt_si_get_sensing_conf_data
*
* SUMMARY: Function to load the sensing information from the system information
* PIP command into the core data struct.
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_si_get_sensing_conf_data(struct pt_core_data *cd)
{
struct pt_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data;
struct pt_sensing_conf_data_dev *scd_dev;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID)
scd_dev =
(struct pt_sensing_conf_data_dev *)
&cd->response_buf[HID_SYSINFO_SENSING_OFFSET];
else
scd_dev =
(struct pt_sensing_conf_data_dev *)
&cd->response_buf[PIP1_SYSINFO_SENSING_OFFSET];
scd->electrodes_x = scd_dev->electrodes_x;
scd->electrodes_y = scd_dev->electrodes_y;
scd->origin_x = scd_dev->origin_x;
scd->origin_y = scd_dev->origin_y;
/* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */
if (scd->origin_x & 0x02) {
scd->tx_num = scd->electrodes_x;
scd->rx_num = scd->electrodes_y;
} else {
scd->tx_num = scd->electrodes_y;
scd->rx_num = scd->electrodes_x;
}
/*
* When the Panel ID is coming from an XY pin and not a dedicated
* GPIO, store the PID in pid_for_loader. This cannot be done for all
* other DUTs as the loader will use cd->pid_for_loader to generate
* the bin file name but will ignore it if pid_for_loader is still
* set to PANEL_ID_NOT_ENABLED
*/
if (cd->panel_id_support &
(PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
mutex_lock(&cd->system_lock);
cd->pid_for_loader = scd_dev->panel_id;
mutex_unlock(&cd->system_lock);
}
scd->panel_id = scd_dev->panel_id;
scd->btn = scd_dev->btn;
scd->scan_mode = scd_dev->scan_mode;
scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
scd->res_x = get_unaligned_le16(&scd_dev->res_x);
scd->res_y = get_unaligned_le16(&scd_dev->res_y);
scd->max_z = get_unaligned_le16(&scd_dev->max_z);
scd->len_x = get_unaligned_le16(&scd_dev->len_x);
scd->len_y = get_unaligned_le16(&scd_dev->len_y);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)scd_dev,
sizeof(struct pt_sensing_conf_data_dev),
"sensing_conf_data");
}
/*******************************************************************************
* FUNCTION: pt_si_setup
*
* SUMMARY: Setup the xy_data and xy_mode by allocating the needed memory
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_si_setup(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
int max_tch = si->sensing_conf_data.max_tch;
if (!si->xy_data)
si->xy_data = kzalloc(max_tch * si->desc.tch_record_size,
GFP_KERNEL);
if (!si->xy_data)
return -ENOMEM;
if (!si->xy_mode)
si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL);
if (!si->xy_mode) {
kfree(si->xy_data);
return -ENOMEM;
}
return 0;
}
/*******************************************************************************
* FUNCTION: pt_si_get_btn_data
*
* SUMMARY: Setup the core data button information based on the response of the
* System Information PIP command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_si_get_btn_data(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
int num_btns = 0;
int num_defined_keys;
u16 *key_table;
int btn;
int i;
int rc = 0;
unsigned int btns;
size_t btn_keys_size;
pt_debug(cd->dev, DL_INFO, "%s: get btn data\n", __func__);
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID)
btns = cd->response_buf[HID_SYSINFO_BTN_OFFSET]
& PIP1_SYSINFO_BTN_MASK;
else
btns = cd->response_buf[PIP1_SYSINFO_BTN_OFFSET]
& PIP1_SYSINFO_BTN_MASK;
for (i = 0; i < PIP1_SYSINFO_MAX_BTN; i++) {
if (btns & (1 << i))
num_btns++;
}
si->num_btns = num_btns;
if (num_btns) {
btn_keys_size = num_btns * sizeof(struct pt_btn);
if (!si->btn)
si->btn = kzalloc(btn_keys_size, GFP_KERNEL);
if (!si->btn)
return -ENOMEM;
if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS] == NULL)
num_defined_keys = 0;
else if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS]->data == NULL)
num_defined_keys = 0;
else
num_defined_keys = cd->cpdata->sett
[PT_IC_GRPNUM_BTN_KEYS]->size;
for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) {
key_table = (u16 *)cd->cpdata->sett
[PT_IC_GRPNUM_BTN_KEYS]->data;
si->btn[btn].key_code = key_table[btn];
si->btn[btn].enabled = true;
}
for (; btn < num_btns; btn++) {
si->btn[btn].key_code = KEY_RESERVED;
si->btn[btn].enabled = true;
}
return rc;
}
kfree(si->btn);
si->btn = NULL;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_si_put_log_data
*
* SUMMARY: Prints all sys info data to kmsg log
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_si_put_log_data(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
struct pt_ttdata *ttdata = &si->ttdata;
struct pt_sensing_conf_data *scd = &si->sensing_conf_data;
int i;
pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_major = 0x%02X (%d)\n",
__func__, ttdata->pip_ver_major, ttdata->pip_ver_major);
pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_minor = 0x%02X (%d)\n",
__func__, ttdata->pip_ver_minor, ttdata->pip_ver_minor);
pt_debug(cd->dev, DL_DEBUG, "%s: fw_pid = 0x%04X (%d)\n",
__func__, ttdata->fw_pid, ttdata->fw_pid);
pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_major = 0x%02X (%d)\n",
__func__, ttdata->fw_ver_major, ttdata->fw_ver_major);
pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_minor = 0x%02X (%d)\n",
__func__, ttdata->fw_ver_minor, ttdata->fw_ver_minor);
pt_debug(cd->dev, DL_DEBUG, "%s: revctrl = 0x%08X (%d)\n",
__func__, ttdata->revctrl, ttdata->revctrl);
pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_conf = 0x%04X (%d)\n",
__func__, ttdata->fw_ver_conf, ttdata->fw_ver_conf);
pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_major = 0x%02X (%d)\n",
__func__, ttdata->bl_ver_major, ttdata->bl_ver_major);
pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_minor = 0x%02X (%d)\n",
__func__, ttdata->bl_ver_minor, ttdata->bl_ver_minor);
pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_h = 0x%04X (%d)\n",
__func__, ttdata->jtag_id_h, ttdata->jtag_id_h);
pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_l = 0x%04X (%d)\n",
__func__, ttdata->jtag_id_l, ttdata->jtag_id_l);
for (i = 0; i < PT_NUM_MFGID; i++)
pt_debug(cd->dev, DL_DEBUG,
"%s: mfg_id[%d] = 0x%02X (%d)\n",
__func__, i, ttdata->mfg_id[i],
ttdata->mfg_id[i]);
pt_debug(cd->dev, DL_DEBUG, "%s: post_code = 0x%04X (%d)\n",
__func__, ttdata->post_code, ttdata->post_code);
pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_x = 0x%02X (%d)\n",
__func__, scd->electrodes_x, scd->electrodes_x);
pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_y = 0x%02X (%d)\n",
__func__, scd->electrodes_y, scd->electrodes_y);
pt_debug(cd->dev, DL_DEBUG, "%s: len_x = 0x%04X (%d)\n",
__func__, scd->len_x, scd->len_x);
pt_debug(cd->dev, DL_DEBUG, "%s: len_y = 0x%04X (%d)\n",
__func__, scd->len_y, scd->len_y);
pt_debug(cd->dev, DL_DEBUG, "%s: res_x = 0x%04X (%d)\n",
__func__, scd->res_x, scd->res_x);
pt_debug(cd->dev, DL_DEBUG, "%s: res_y = 0x%04X (%d)\n",
__func__, scd->res_y, scd->res_y);
pt_debug(cd->dev, DL_DEBUG, "%s: max_z = 0x%04X (%d)\n",
__func__, scd->max_z, scd->max_z);
pt_debug(cd->dev, DL_DEBUG, "%s: origin_x = 0x%02X (%d)\n",
__func__, scd->origin_x, scd->origin_x);
pt_debug(cd->dev, DL_DEBUG, "%s: origin_y = 0x%02X (%d)\n",
__func__, scd->origin_y, scd->origin_y);
pt_debug(cd->dev, DL_DEBUG, "%s: panel_id = 0x%02X (%d)\n",
__func__, scd->panel_id, scd->panel_id);
pt_debug(cd->dev, DL_DEBUG, "%s: btn =0x%02X (%d)\n",
__func__, scd->btn, scd->btn);
pt_debug(cd->dev, DL_DEBUG, "%s: scan_mode = 0x%02X (%d)\n",
__func__, scd->scan_mode, scd->scan_mode);
pt_debug(cd->dev, DL_DEBUG,
"%s: max_num_of_tch_per_refresh_cycle = 0x%02X (%d)\n",
__func__, scd->max_tch, scd->max_tch);
pt_debug(cd->dev, DL_DEBUG, "%s: xy_mode = %p\n",
__func__, si->xy_mode);
pt_debug(cd->dev, DL_DEBUG, "%s: xy_data = %p\n",
__func__, si->xy_data);
}
/*******************************************************************************
* FUNCTION: pt_get_sysinfo_regs
*
* SUMMARY: Setup all the core data System information based on the response
* of the System Information PIP command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_get_sysinfo_regs(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
int rc;
rc = pt_si_get_btn_data(cd);
if (rc < 0)
return rc;
pt_si_get_ttdata(cd);
pt_si_get_sensing_conf_data(cd);
pt_si_setup(cd);
pt_si_put_log_data(cd);
si->ready = true;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_free_si_ptrs
*
* SUMMARY: Frees all memory associated with the System Information within
* core data
*
* RETURN: n/a
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_free_si_ptrs(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
kfree(si->btn);
kfree(si->xy_mode);
kfree(si->xy_data);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_get_sysinfo_
*
* SUMMARY: Sends the PIP Get SYS INFO command to the DUT and waits for the
* response.
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd)
{
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
/* Parse the sysinfo data */
rc = pt_get_sysinfo_regs(cd);
if (rc)
pt_free_si_ptrs(cd);
return rc;
}
#ifndef TTDL_KERNEL_SUBMISSION
/*******************************************************************************
* FUNCTION: pt_hid_output_get_sysinfo
*
* SUMMARY: Protected call to pt_hid_output_get_sysinfo_
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_hid_output_get_sysinfo(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_get_sysinfo_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
#endif /*!TTDL_KERNEL_SUBMISSION */
/*******************************************************************************
* FUNCTION: pt_pip_suspend_scanning_
*
* SUMMARY: Sends the PIP Suspend Scanning command to the DUT
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip_suspend_scanning_(struct pt_core_data *cd)
{
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SUSPEND_SCANNING),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Suspend Scan PIP cmd failed. rc = %d\n",
__func__, rc);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_suspend_scanning
*
* SUMMARY: Protected wrapper for calling pt_hid_output_suspend_scanning_
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip_suspend_scanning(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_suspend_scanning_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_suspend_scanning
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_suspend_scanning
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - 0 = call non-protected function
* 1 = call protected function
******************************************************************************/
static int _pt_request_pip_suspend_scanning(struct device *dev,
int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_suspend_scanning(cd);
return pt_pip_suspend_scanning_(cd);
}
/*******************************************************************************
* FUNCTION: pt_pip_resume_scanning_
*
* SUMMARY: Sends the PIP Resume Scanning command to the DUT
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip_resume_scanning_(struct pt_core_data *cd)
{
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RESUME_SCANNING),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Resume Scan PIP cmd failed. rc = %d\n",
__func__, rc);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_resume_scanning
*
* SUMMARY: Protected wrapper for calling pt_pip_resume_scanning_
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip_resume_scanning(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_resume_scanning_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_resume_scanning
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_resume_scanning
*
* RETURN::
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - 0 = call non-protected function
* 1 = call protected function
******************************************************************************/
static int _pt_request_pip_resume_scanning(struct device *dev,
int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_resume_scanning(cd);
return pt_pip_resume_scanning_(cd);
}
/*******************************************************************************
* FUNCTION: pt_pip_get_param_
*
* SUMMARY: Sends a PIP command 0x05 Get Parameter to the DUT and returns
* the 32bit parameter value
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* param_id - parameter ID to retrieve
* *value - value of DUT parameter
******************************************************************************/
static int pt_pip_get_param_(struct pt_core_data *cd,
u8 param_id, u32 *value)
{
int write_length = 1;
u8 param[1] = { param_id };
u8 read_param_id;
int param_size;
u8 *ptr;
int rc = 0;
int i;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_PARAM),
.write_length = write_length,
.write_buf = param,
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
read_param_id = cd->response_buf[6];
param_size = cd->response_buf[7];
ptr = &cd->response_buf[8];
} else {
read_param_id = cd->response_buf[5];
param_size = cd->response_buf[6];
ptr = &cd->response_buf[7];
}
if (read_param_id != param_id)
return -EPROTO;
*value = 0;
for (i = 0; i < param_size; i++)
*value += ptr[i] << (i * 8);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_get_param
*
* SUMMARY: Protected call to pt_hid_output_get_param_ by a request exclusive
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* param_id - parameter ID to retrieve
* *value - value of DUT parameter
******************************************************************************/
static int pt_pip_get_param(struct pt_core_data *cd,
u8 param_id, u32 *value)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_get_param_(cd, param_id, value);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_get_param
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_get_param
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non protected function
* param_id - parameter ID to retrieve
* *value - value of DUT parameter
******************************************************************************/
int _pt_request_pip_get_param(struct device *dev,
int protect, u8 param_id, u32 *value)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_get_param(cd, param_id, value);
return pt_pip_get_param_(cd, param_id, value);
}
/*******************************************************************************
* FUNCTION: pt_pip_set_param_
*
* SUMMARY: Sends a PIP command 0x06 Set Parameter to the DUT writing the
* passed in value to flash
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* param_id - parameter ID to set
* value - value to write
* size - size to write
******************************************************************************/
static int pt_pip_set_param_(struct pt_core_data *cd,
u8 param_id, u32 value, u8 size)
{
u8 write_buf[6];
u8 *ptr = &write_buf[2];
int rc = 0;
int i;
u8 read_param_id = 0;
u8 read_size = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM),
.write_buf = write_buf,
};
write_buf[0] = param_id;
write_buf[1] = size;
for (i = 0; i < size; i++) {
ptr[i] = value & 0xFF;
value = value >> 8;
}
pip1_cmd.write_length = 2 + size;
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
read_param_id = cd->response_buf[6];
read_size = cd->response_buf[7];
} else {
read_param_id = cd->response_buf[5];
read_size = cd->response_buf[6];
}
if (param_id != read_param_id || size != read_size)
return -EPROTO;
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_set_param
*
* SUMMARY: Protected call to pt_hid_output_set_param_ by a request exclusive
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* param_id - parameter ID to set
* value - value to write
* size - size to write
******************************************************************************/
static int pt_pip_set_param(struct pt_core_data *cd,
u8 param_id, u32 value, u8 size)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_set_param_(cd, param_id, value, size);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_set_param
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_set_param
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* param_id - parameter ID to set
* value - value to write
* size - size to write
******************************************************************************/
int _pt_request_pip_set_param(struct device *dev, int protect,
u8 param_id, u32 value, u8 size)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_set_param(cd, param_id, value, size);
return pt_pip_set_param_(cd, param_id, value, size);
}
/*******************************************************************************
* FUNCTION: _pt_pip_enter_easywake_state_
*
* SUMMARY: Sends a PIP command 0x09 Enter EasyWake State to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* data - easywake guesture (Only used for PIP1.6 and earlier)
* *return_data - return status if easywake was entered
******************************************************************************/
static int pt_hid_output_enter_easywake_state_(
struct pt_core_data *cd, u8 data, u8 *return_data)
{
int write_length = 1;
u8 param[1] = { data };
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_ENTER_EASYWAKE_STATE),
.write_length = write_length,
.write_buf = param,
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
*return_data = cd->response_buf[5];
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_verify_config_block_crc_
*
* SUMMARY: Sends the PIP "Verify Data Block CRC" (0x20) command to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* ebid - enumerated block ID
* *status - PIP command status
* calculated_crc - calculated CRC
* stored_crc - stored CRC in config area
******************************************************************************/
static int pt_pip_verify_config_block_crc_(
struct pt_core_data *cd, u8 ebid, u8 *status,
u16 *calculated_crc, u16 *stored_crc)
{
int write_length = 1;
u8 param[1] = { ebid };
u8 *ptr;
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
.write_length = write_length,
.write_buf = param,
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
ptr = &cd->response_buf[5];
*status = ptr[0];
*calculated_crc = get_unaligned_le16(&ptr[1]);
*stored_crc = get_unaligned_le16(&ptr[3]);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_verify_config_block_crc
*
* SUMMARY: Protected call to pt_hid_output_verify_config_block_crc_() within
* an exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* ebid - enumerated block ID
* *status - PIP command status
* calculated_crc - calculated CRC
* stored_crc - stored CRC in config area
******************************************************************************/
static int pt_pip_verify_config_block_crc(
struct pt_core_data *cd, u8 ebid, u8 *status,
u16 *calculated_crc, u16 *stored_crc)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_verify_config_block_crc_(cd, ebid, status,
calculated_crc, stored_crc);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_verify_config_block_crc
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip_verify_config_block_crc_
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* ebid - enumerated block ID
* *status - PIP command status
* calculated_crc - calculated CRC
* stored_crc - stored CRC in config area
******************************************************************************/
static int _pt_request_pip_verify_config_block_crc(
struct device *dev, int protect, u8 ebid, u8 *status,
u16 *calculated_crc, u16 *stored_crc)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_verify_config_block_crc(cd, ebid,
status, calculated_crc, stored_crc);
return pt_pip_verify_config_block_crc_(cd, ebid,
status, calculated_crc, stored_crc);
}
/*******************************************************************************
* FUNCTION: pt_pip_get_config_row_size_
*
* SUMMARY: Sends the PIP "Get Data Row Size" (0x21) command to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* protect - flag to call protected or non-protected
* *row_size - pointer to store the retrieved row size
******************************************************************************/
static int pt_pip_get_config_row_size_(struct pt_core_data *cd,
u16 *row_size)
{
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_CONFIG_ROW_SIZE),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID)
*row_size = get_unaligned_le16(&cd->response_buf[6]);
else
*row_size = get_unaligned_le16(&cd->response_buf[5]);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_get_config_row_size
*
* SUMMARY: Protected call to pt_hid_output_get_config_row_size_ within
* an exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* protect - flag to call protected or non-protected
* *row_size - pointer to store the retrieved row size
******************************************************************************/
static int pt_pip_get_config_row_size(struct pt_core_data *cd,
u16 *row_size)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_get_config_row_size_(cd, row_size);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_get_config_row_size
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip_get_config_row_size_
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* *row_size - pointer to store the retrieved row size
******************************************************************************/
static int _pt_request_pip_get_config_row_size(struct device *dev,
int protect, u16 *row_size)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_get_config_row_size(cd, row_size);
return pt_pip_get_config_row_size_(cd, row_size);
}
/*******************************************************************************
* FUNCTION: pt_pip1_read_data_block_
*
* SUMMARY: Sends the PIP "Read Data Block" (0x22) command to the DUT and print
* output data to the "read_buf" and update "crc".
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* row_number - row number
* length - length of data to read
* ebid - block id
* *actual_read_len - Actual data length read
* *read_buf - pointer to the buffer to store read data
* read_buf_size - size of read_buf
* *crc - pointer to store CRC of row data
******************************************************************************/
static int pt_pip1_read_data_block_(struct pt_core_data *cd,
u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
u8 *read_buf, u16 read_buf_size, u16 *crc)
{
int read_ebid;
int status;
int rc = 0;
int write_length = 5;
u8 write_buf[5];
u8 cmd_offset = 0;
u16 calc_crc;
u16 rsp_len;
u8 pip3_exofs = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_READ_DATA_BLOCK),
.write_length = write_length,
.write_buf = write_buf,
};
write_buf[cmd_offset++] = LOW_BYTE(row_number);
write_buf[cmd_offset++] = HI_BYTE(row_number);
write_buf[cmd_offset++] = LOW_BYTE(length);
write_buf[cmd_offset++] = HI_BYTE(length);
write_buf[cmd_offset++] = ebid;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
cd->force_pip3_report_type = true;
pip3_exofs = 1;
}
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
status = cd->response_buf[5 - pip3_exofs];
if (status) {
rc = status;
goto exit;
}
read_ebid = cd->response_buf[6 - pip3_exofs];
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
if (read_ebid != ebid) {
rc = -EPROTO;
goto exit;
}
} else {
if ((read_ebid != ebid) || (cd->response_buf[9] != 0)) {
rc = -EPROTO;
goto exit;
}
}
rsp_len = get_unaligned_le16(&cd->response_buf[0]);
/*
* For PIP3/HID command 0x22, the offset of ARL filed in
* the response payload is 6, so PIP1 offset (7) needs
* to be decremented by 1.
*/
*actual_read_len = get_unaligned_le16(
&cd->response_buf[7 - pip3_exofs]);
pt_debug(cd->dev, DL_INFO,
"%s: Resp Len=0x%04X Actual Read Length=0x%04X\n",
__func__, rsp_len, *actual_read_len);
if (length == 0 || *actual_read_len == 0)
goto exit;
/* Do not continue if the ARL is larger than the response */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(*actual_read_len + 12) != rsp_len) {
/*
* For HID/PIP3 response, command 0x22 payload length is
* 12 bytes larger than ARL.
*/
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid Actual Read Length\n", __func__);
rc = -EBADMSG;
goto exit;
}
/*
* For PIP3/HID command 0x22, the offset of DATA filed in
* the response payload is 8, so PIP1 offset (10) needs
* to be decremented by 2.
*/
if (read_buf_size >= *actual_read_len)
memcpy(read_buf, &cd->response_buf[10 - 2 * pip3_exofs],
*actual_read_len);
else {
rc = -EPROTO;
goto exit;
}
/*
* For PIP3/HID command 0x22, the offset of CRC filed in
* the response payload is (ARL+8), so PIP1 offset (ARL+10)
* needs to be decremented by 2.
*/
*crc = get_unaligned_le16(
&cd->response_buf[*actual_read_len + 10 - 2 * pip3_exofs]);
/* Validate Row Data CRC */
calc_crc = _pt_compute_crc(read_buf, *actual_read_len);
if (*crc == calc_crc) {
goto exit;
} else {
pt_debug(cd->dev, DL_ERROR,
"%s: CRC Missmatch packet=0x%04X calc=0x%04X\n",
__func__, *crc, calc_crc);
rc = -EPROTO;
}
exit:
cd->force_pip3_report_type = false;
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_read_data_block
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to pt_pip1_read_data_block_
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* row_number - row number
* length - length of data to read
* ebid - block id
* *actual_read_len - Actual data length read
* *read_buf - pointer to the buffer to store read data
* *crc - pointer to store CRC of row data
******************************************************************************/
static int _pt_request_pip_read_data_block(struct device *dev,
u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
u8 *read_buf, u16 read_buf_size, u16 *crc)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return pt_pip1_read_data_block_(cd, row_number, length,
ebid, actual_read_len, read_buf, read_buf_size, crc);
}
/*
* There is fixed 4 bytes shift, 5 bytes are needed at least if to read
* 1 byte (i.e. MFG_VAL0) from Manufactuary Data Block.
*/
#define MDATA_OFFSET (4)
#define MDATA_READ_SIZE (1)
#define MDATA_READ_BUFFER_SIZE (MDATA_OFFSET + MDATA_READ_SIZE)
/*******************************************************************************
* FUNCTION: _pt_get_pid_from_mfg_data_
*
* SUMMARY: Wrapper function to call pt_pip1_read_data_block_() to get
* Manufacturing Data which stores panel ID information in this case.
*
* NOTE: This function doesn't support PIP3.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *core_data - pointer to core data
* *panel_id - pointer to store Panel ID
******************************************************************************/
static int _pt_get_pid_from_mfg_data_(struct pt_core_data *core_data, u8 *panel_id)
{
int rc;
u8 read_buf[MDATA_READ_BUFFER_SIZE];
u16 crc;
u16 actual_read_len = 0;
if (core_data->protocol_mode == PT_PROTOCOL_MODE_HID)
return -EINVAL;
rc = pt_pip1_read_data_block_(core_data, 0, MDATA_READ_BUFFER_SIZE, PT_MDATA_EBID,
&actual_read_len, read_buf, MDATA_READ_BUFFER_SIZE, &crc);
if (rc)
return rc;
if (panel_id)
*panel_id = read_buf[MDATA_OFFSET];
return 0;
}
/*******************************************************************************
* FUNCTION: pt_get_pid_from_mfg_data_
*
* SUMMARY: This function retrieves MFG_DATA[0] to get panel ID information.
*
* NOTE: The post condition of calling this function will be that the DUT will
* be in SCANNINING mode if no failures occur
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *core_data - pointer to core data
* *panel_id - pointer to store Panel ID
******************************************************************************/
static int pt_get_pid_from_mfg_data_(struct pt_core_data *core_data, u8 *panel_id)
{
int rc;
rc = pt_pip_suspend_scanning_(core_data);
if (rc)
goto error;
rc = _pt_get_pid_from_mfg_data_(core_data, panel_id);
pt_pip_resume_scanning_(core_data);
error:
pt_debug(core_data->dev, DL_INFO, "%s: panel_id:%02X\n", __func__, *panel_id);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip1_write_data_block_
*
* SUMMARY: Sends the PIP "Write Data Block" (0x23) command to the DUT and
* write data to the data block.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* row_number - row in config block to write to
* write_length - length of data to write
* ebid - enumerated block ID
* *write_buf - pointer to buffer to write
* *security_key - pointer to security key to allow write
* *actual_write_len - pointer to store data length actually written
******************************************************************************/
static int pt_pip1_write_data_block_(struct pt_core_data *cd,
u16 row_number, u16 write_length, u8 ebid, u8 *write_buf,
u8 *security_key, u16 *actual_write_len)
{
/* row_number + write_len + ebid + security_key + crc */
int full_write_length = 2 + 2 + 1 + write_length + 8 + 2;
u8 *full_write_buf;
u8 cmd_offset = 0;
u16 crc;
int status;
int rc = 0;
int read_ebid;
u8 *data;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_WRITE_DATA_BLOCK),
.write_length = full_write_length,
.timeout_ms = PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT,
};
full_write_buf = kzalloc(full_write_length, GFP_KERNEL);
if (!full_write_buf)
return -ENOMEM;
pip1_cmd.write_buf = full_write_buf;
full_write_buf[cmd_offset++] = LOW_BYTE(row_number);
full_write_buf[cmd_offset++] = HI_BYTE(row_number);
full_write_buf[cmd_offset++] = LOW_BYTE(write_length);
full_write_buf[cmd_offset++] = HI_BYTE(write_length);
full_write_buf[cmd_offset++] = ebid;
data = &full_write_buf[cmd_offset];
memcpy(data, write_buf, write_length);
cmd_offset += write_length;
memcpy(&full_write_buf[cmd_offset], security_key, 8);
cmd_offset += 8;
crc = _pt_compute_crc(data, write_length);
full_write_buf[cmd_offset++] = LOW_BYTE(crc);
full_write_buf[cmd_offset++] = HI_BYTE(crc);
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
status = cd->response_buf[5];
if (status) {
rc = -EINVAL;
goto exit;
}
read_ebid = cd->response_buf[6];
if (read_ebid != ebid) {
rc = -EPROTO;
goto exit;
}
*actual_write_len = get_unaligned_le16(&cd->response_buf[7]);
exit:
kfree(full_write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_write_data_block
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip1_write_data_block_
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* row_number - row in config block to write to
* write_length - length of data to write
* ebid - enumerated block ID
* *write_buf - pointer to buffer to write
* *security_key - pointer to security key to allow write
* *actual_write_len - pointer to store data length actually written
******************************************************************************/
static int _pt_request_pip_write_data_block(struct device *dev,
u16 row_number, u16 write_length, u8 ebid,
u8 *write_buf, u8 *security_key, u16 *actual_write_len)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return pt_pip1_write_data_block_(cd, row_number,
write_length, ebid, write_buf, security_key,
actual_write_len);
}
/*******************************************************************************
* FUNCTION: pt_pip_get_data_structure_
*
* SUMMARY: Sends the PIP "Retrieve Data Structure" (0x24) command to the DUT
* returning a structure of data defined by data_id
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_offset - read pointer offset
* read_length - length of data to read
* data_id - data ID to read
* *status - pointer to store the read response status
* *data_format - pointer to store format of data read
* *actual_read_len - pointer to store data length actually read
* *data - pointer to store data read
******************************************************************************/
static int pt_pip_get_data_structure_(
struct pt_core_data *cd, u16 read_offset, u16 read_length,
u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
u8 *data)
{
int rc = 0;
u16 total_read_len = 0;
u16 rsp_len;
u16 read_len;
u16 off_buf = 0;
u8 write_buf[5];
u8 read_data_id;
u8 pip3_exofs = 0;
u8 data_elem_size;
int data_size;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_DATA_STRUCTURE),
.write_length = 5,
.write_buf = write_buf,
};
again:
write_buf[0] = LOW_BYTE(read_offset);
write_buf[1] = HI_BYTE(read_offset);
write_buf[2] = LOW_BYTE(read_length);
write_buf[3] = HI_BYTE(read_length);
write_buf[4] = data_id;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
cd->force_pip3_report_type = true;
pip3_exofs = 1;
}
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
if (cd->response_buf[5 - pip3_exofs] != PT_CMD_STATUS_SUCCESS)
goto set_status;
read_data_id = cd->response_buf[6 - pip3_exofs];
if (read_data_id != data_id) {
rc = -EPROTO;
goto exit;
}
rsp_len = get_unaligned_le16(&cd->response_buf[0]);
read_len = get_unaligned_le16(&cd->response_buf[7]);
data_elem_size = cd->response_buf[9] & 0x07;
data_size = read_len * data_elem_size;
pt_debug(cd->dev, DL_INFO,
"%s: %s=0x%04X %s=0x%04X %s=0x%02X %s=0x%04X\n",
__func__, "Resp Len", rsp_len,
"Actual Read Length", read_len,
"Element Size", data_elem_size,
"Calc Data Size", data_size);
/* Do not continue if the ARL is larger than the response */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(data_size + 12) != rsp_len) {
/*
* For HID/PIP3 response, command 0x24 payload length
* is 12 bytes larger than ARL * Element Size.
*/
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid Actual Read Length\n", __func__);
rc = -EBADMSG;
goto exit;
}
if (read_len && data) {
memcpy(&data[off_buf],
&cd->response_buf[10], read_len);
total_read_len += read_len;
if (read_len < read_length &&
cd->protocol_mode != PT_PROTOCOL_MODE_HID) {
read_offset += read_len;
off_buf += read_len;
read_length -= read_len;
goto again;
}
}
if (data_format)
*data_format = cd->response_buf[9];
if (actual_read_len)
*actual_read_len = total_read_len;
set_status:
if (status)
*status = cd->response_buf[5 - pip3_exofs];
exit:
cd->force_pip3_report_type = false;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_get_data_structure
*
* SUMMARY: Protected call to pt_hid_output_get_data_structure within
* an exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_offset - read pointer offset
* read_length - length of data to read
* data_id - data ID to read
* *status - pointer to store the read response status
* *data_format - pointer to store format of data read
* *actual_read_len - pointer to store data length actually read
* *data - pointer to store data read
******************************************************************************/
static int pt_pip_get_data_structure(
struct pt_core_data *cd, u16 read_offset, u16 read_length,
u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
u8 *data)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_get_data_structure_(cd, read_offset,
read_length, data_id, status, data_format,
actual_read_len, data);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_get_data_structure
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip_get_data_structure
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* read_offset - read pointer offset
* read_length - length of data to read
* data_id - data ID to read
* *status - pointer to store the read response status
* *data_format - pointer to store format of data read
* *actual_read_len - pointer to store data length actually read
* *data - pointer to store data read
******************************************************************************/
static int _pt_request_pip_get_data_structure(struct device *dev,
int protect, u16 read_offset, u16 read_length, u8 data_id,
u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_get_data_structure(cd,
read_offset, read_length, data_id, status,
data_format, actual_read_len, data);
return pt_pip_get_data_structure_(cd,
read_offset, read_length, data_id, status,
data_format, actual_read_len, data);
}
/*******************************************************************************
* FUNCTION: _pt_manage_local_cal_data
*
* SUMMARY: This function manages storing or restoring a copy of the Firmware
* CALIBRATION data. It stores it in a local static array and can be
* cleared, loaded or used to restore the CAL data back to the running FW.
* The CAL data is read or restored by use of the PIP1 commands:
* - READ_DATA_BLOCK (0x22)
* - WRITE_DATA_BLOCK (0x23)
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* action - One of the following actions:
* - PT_CAL_DATA_SAVE
* - PT_CAL_DATA_RESTORE
* - PT_CAL_DATA_CLEAR
* - PT_CAL_DATA_SIZE
* *size - pointer to the number of bytes transfered
* *crc - pointer to Chip ID CRC that the CAL data was retrieved from
******************************************************************************/
static int _pt_manage_local_cal_data(struct device *dev, u8 action, u16 *size,
unsigned short *crc)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
unsigned short calc_id_crc = 0;
static u8 *cal_cache_data;
static u16 cal_cache_len;
static unsigned short cal_cache_chip_id;
int rc = 0;
u8 *tmp_data = NULL;
u8 row_number = 0;
u8 prefix[20];
u16 cal_size = 0;
u16 transfer_size;
u16 act_trans_len = 0;
u16 byte_offset = 0;
u16 cal_blk_size;
u16 total_rows;
u16 remain_bytes;
u16 data_block_crc;
u16 buf_size = 12;
pt_debug(dev, DL_INFO, "%s: ATM - CAL Cache action=%d\n",
__func__, action);
switch (action) {
case PT_CAL_DATA_SAVE:
/* Read the size of the CAL block and calculate # rows */
tmp_data = kzalloc(buf_size, GFP_KERNEL);
if (!tmp_data) {
rc = -ENOMEM;
goto exit;
}
/*
* Don't check rc as doing a read size will give a false
* error on the CRC check.
*/
rc = pt_pip1_read_data_block_(cd, row_number, 0, PT_CAL_EBID,
&act_trans_len, tmp_data, buf_size, &data_block_crc);
cal_blk_size = act_trans_len;
kfree(tmp_data);
pt_debug(dev, DL_INFO,
"%s: CAL Cache size=%d FW CAL Size=%d\n",
__func__, cal_cache_len, cal_blk_size);
/* Safety net to ensure we didn't read incorrect size */
if (cal_blk_size > PT_CAL_DATA_MAX_SIZE) {
pt_debug(dev, DL_ERROR, "%s: Alloc struct Failed\n",
__func__);
rc = 1;
goto exit;
}
/* Panels could have diff CAL sizes, Re-allocate the cache */
if (cal_blk_size != cal_cache_len) {
kfree(cal_cache_data);
cal_cache_data = kzalloc(cal_blk_size + 2,
GFP_KERNEL);
if (!cal_cache_data) {
rc = -ENOMEM;
goto exit;
}
pt_debug(dev, DL_INFO, "%s: CAL Cache Allocated\n",
__func__);
}
memset(&cal_cache_data[0], 0, cal_blk_size + 2);
/* Calculate how many rows [0-n] (PIP Transactions) */
total_rows = (cal_blk_size / PT_CAL_DATA_ROW_SIZE) - 1;
remain_bytes = cal_blk_size % PT_CAL_DATA_ROW_SIZE;
/* Add row if we have a last partial row */
if (remain_bytes > 0)
total_rows++;
pt_debug(dev, DL_INFO,
"%s: CAL size=%d rows=[0-%d] partial row bytes=%d\n",
__func__, cal_blk_size, total_rows, remain_bytes);
/* Read all rows unless an error occurs */
rc = 0;
while (rc == 0 && row_number <= total_rows) {
act_trans_len = 0;
if (remain_bytes > 0 && row_number == total_rows)
transfer_size = remain_bytes;
else
transfer_size = PT_CAL_DATA_ROW_SIZE;
rc = pt_pip1_read_data_block_(cd, row_number,
transfer_size, PT_CAL_EBID,
&act_trans_len,
&cal_cache_data[byte_offset], cal_blk_size + 2,
&data_block_crc);
if (rc) {
/* Error occured, exit loop */
cal_size = 0;
break;
}
pt_debug(dev, DL_INFO,
"%s: CAL read rc=%d actual read len=%d\n",
__func__, rc, act_trans_len);
byte_offset += act_trans_len;
cal_size = byte_offset;
scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
pt_pr_buf(dev, DL_INFO,
&cal_cache_data[byte_offset - act_trans_len],
act_trans_len, prefix);
row_number++;
}
if (cal_size > 0) {
/* Save a CRC of the chip info the CAL was saved from */
calc_id_crc = crc_ccitt_calculate(
(u8 *)&ttdata->chip_rev, 4 + PT_UID_SIZE);
cal_cache_chip_id = calc_id_crc;
cal_cache_len = cal_size;
pt_debug(dev, DL_INFO,
"%s: CAL Cache: CRC=0x%04X Total Size=%d\n",
__func__, calc_id_crc, cal_size);
}
*size = cal_size;
*crc = calc_id_crc;
break;
case PT_CAL_DATA_RESTORE:
cal_size = cal_cache_len;
while ((rc == 0) && (byte_offset < cal_size)) {
if (cal_size - byte_offset > PT_CAL_DATA_ROW_SIZE)
transfer_size = PT_CAL_DATA_ROW_SIZE;
else
transfer_size = cal_size - byte_offset;
rc = pt_pip1_write_data_block_(cd, row_number,
transfer_size, PT_CAL_EBID,
&cal_cache_data[byte_offset],
(u8 *)pt_data_block_security_key,
&act_trans_len);
byte_offset += act_trans_len;
pt_debug(dev, DL_INFO, "%s: CAL write byte offset=%d\n",
__func__, byte_offset);
scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
pt_pr_buf(dev, DL_INFO,
&cal_cache_data[byte_offset - act_trans_len],
act_trans_len, prefix);
if ((byte_offset > cal_size) ||
(act_trans_len != transfer_size))
rc = -EIO;
row_number++;
}
*size = byte_offset;
*crc = cal_cache_chip_id;
break;
case PT_CAL_DATA_CLEAR:
if (cal_cache_data)
memset(&cal_cache_data[0], 0, cal_cache_len);
cal_cache_len = 0;
cal_cache_chip_id = 0;
*size = 0;
*crc = 0;
break;
case PT_CAL_DATA_INFO:
default:
*size = cal_cache_len;
*crc = cal_cache_chip_id;
pt_debug(dev, DL_INFO,
"%s: CAL Cache: CRC=%04X Total Size=%d\n",
__func__, cal_cache_chip_id,
cal_cache_len);
break;
}
exit:
pt_debug(dev, DL_INFO,
"%s: CAL Cache exit: rc=%d CRC=0x%04X Total Size=%d\n",
__func__, rc, *crc, *size);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_run_selftest_
*
* SUMMARY: Sends the PIP "Run Self Test" (0x26) command to the DUT
* to execute a FW built in self test
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* test_id - enumerated test ID to run
* write_idacs_to_flash - flag whether to write new IDACS to flash
* *status - pointer to store the read response status
* *summary_results - pointer to store the results summary
* *results_available - pointer to store if results are available
*****************************************************************************/
static int pt_pip_run_selftest_(
struct pt_core_data *cd, u8 test_id,
u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
u8 *results_available)
{
int rc = 0;
u8 write_buf[2];
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RUN_SELF_TEST),
.write_length = 2,
.write_buf = write_buf,
.timeout_ms = PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT,
};
write_buf[0] = test_id;
write_buf[1] = write_idacs_to_flash;
if (cd->active_dut_generation == DUT_PIP2_CAPABLE)
pip1_cmd.write_length = 1;
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (status)
*status = cd->response_buf[5];
if (cd->protocol_mode != PT_PROTOCOL_MODE_HID) {
if (summary_result)
*summary_result = cd->response_buf[6];
/* results_available only available before PIP 1.03 */
if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 3)) {
if (results_available)
*results_available = cd->response_buf[7];
}
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_run_selftest
*
* SUMMARY: Protected call to pt_hid_output_run_selftest within
* an exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* test_id - enumerated test ID to run
* write_idacs_to_flash - flag whether to write new IDACS to flash
* *status - pointer to store the read response status
* *summary_results - pointer to store the results summary
* *results_available - pointer to store if results are available
******************************************************************************/
static int pt_pip_run_selftest(
struct pt_core_data *cd, u8 test_id,
u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
u8 *results_available)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_run_selftest_(cd, test_id,
write_idacs_to_flash, status, summary_result,
results_available);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_run_selftest
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_run_selftest
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* test_id - enumerated test ID to run
* write_idacs_to_flash - flag whether to write new IDACS to flash
* *status - pointer to store the read response status
* *summary_results - pointer to store the results summary
* *results_available - pointer to store if results are available
******************************************************************************/
static int _pt_request_pip_run_selftest(struct device *dev,
int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status,
u8 *summary_result, u8 *results_available)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_run_selftest(cd, test_id,
write_idacs_to_flash, status, summary_result,
results_available);
return pt_pip_run_selftest_(cd, test_id,
write_idacs_to_flash, status, summary_result,
results_available);
}
/*******************************************************************************
* FUNCTION: _pt_pip_get_selftest_result_
*
* SUMMARY: Sends the PIP "Get Self Test Results" (0x27) command to the DUT
* to retrieve the self test results from the self test already executed
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_offset - read pointer offset
* read_length - length of data to read
* test_id - enumerated test ID to read selftest results from
* *status - pointer to store the read response status
* *actual_read_len - pointer to store data length actually read
* *status - pointer to where the cmd response statas is stored
******************************************************************************/
static int pt_pip_get_selftest_result_(
struct pt_core_data *cd, u16 read_offset, u16 read_length,
u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
{
int rc = 0;
u16 total_read_len = 0;
u16 rsp_len;
u16 read_len;
u16 off_buf = 0;
u8 write_buf[5];
u8 read_test_id;
u8 pip3_exofs = 0;
bool repeat;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SELF_TEST_RESULT),
.write_length = 5,
.write_buf = write_buf,
};
/*
* Do not repeat reading for Auto Shorts test
* when PIP version < 1.3
*/
repeat = (IS_PIP_VER_GE(&cd->sysinfo, 1, 3)
|| test_id != PT_ST_ID_AUTOSHORTS)
&& cd->protocol_mode != PT_PROTOCOL_MODE_HID;
again:
write_buf[0] = LOW_BYTE(read_offset);
write_buf[1] = HI_BYTE(read_offset);
write_buf[2] = LOW_BYTE(read_length);
write_buf[3] = HI_BYTE(read_length);
write_buf[4] = test_id;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
cd->force_pip3_report_type = true;
pip3_exofs = 1;
}
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
if (cd->response_buf[5 - pip3_exofs] != PT_CMD_STATUS_SUCCESS)
goto set_status;
read_test_id = cd->response_buf[6 - pip3_exofs];
if (read_test_id != test_id) {
rc = -EPROTO;
goto exit;
}
rsp_len = get_unaligned_le16(&cd->response_buf[0]);
read_len = get_unaligned_le16(&cd->response_buf[7]);
pt_debug(cd->dev, DL_INFO,
"%s: Resp Len=0x%04X Actual Read Length=0x%04X\n",
__func__, rsp_len, read_len);
/* Do not continue if the ARL is larger than the response */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(read_len + 11) != rsp_len) {
/*
* For HID/PIP3 response, payload length is 11 bytes
* larger than ARL.
*/
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid Actual Read Length\n", __func__);
rc = -EBADMSG;
goto exit;
}
if (read_len && data) {
memcpy(&data[off_buf],
&cd->response_buf[10 - pip3_exofs], read_len);
total_read_len += read_len;
if (repeat && read_len < read_length) {
read_offset += read_len;
off_buf += read_len;
read_length -= read_len;
goto again;
}
}
if (actual_read_len)
*actual_read_len = total_read_len;
set_status:
if (status)
*status = cd->response_buf[5 - pip3_exofs];
exit:
cd->force_pip3_report_type = false;
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_pip_get_selftest_result
*
* SUMMARY: Protected call to pt_hid_output_get_selftest_result by exclusive
* access to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* read_offset - read pointer offset
* read_length - length of data to read
* test_id - enumerated test ID to read selftest results from
* *status - pointer to store the read response status
* *actual_read_len - pointer to store data length actually read
* *status - pointer to where the cmd response statas is stored
******************************************************************************/
static int pt_pip_get_selftest_result(
struct pt_core_data *cd, u16 read_offset, u16 read_length,
u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_get_selftest_result_(cd, read_offset,
read_length, test_id, status, actual_read_len, data);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_get_selftest_result
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_get_selftest_result
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* read_offset - read pointer offset
* read_length - length of data to read
* test_id - enumerated test ID to read selftest results from
* *status - pointer to store the read response status
* *actual_read_len - pointer to store data length actually read
* *data - pointer to where the data read is stored
******************************************************************************/
static int _pt_request_pip_get_selftest_result(struct device *dev,
int protect, u16 read_offset, u16 read_length, u8 test_id,
u8 *status, u16 *actual_read_len, u8 *data)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_get_selftest_result(cd, read_offset,
read_length, test_id, status, actual_read_len,
data);
return pt_pip_get_selftest_result_(cd, read_offset,
read_length, test_id, status, actual_read_len,
data);
}
/*******************************************************************************
* FUNCTION: _pt_pip_load_self_test_param
*
* SUMMARY: Sends the PIP "Load Self Test Parameters" (0x25) command to the DUT
* to load paramters needed by a self test
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* self_test_id - enumerated test ID for which the parmeters belong
* load_offset - mem offset to where to load parameters
* load_length - length of parameter data to load
* *parameters - pointer to list of parameter data
* *status - pointer to store the response status
* *ret_test_id - pointer to returned test id the paramters were stored
* *act_load_len - pointer to store the actual load length that was writen
******************************************************************************/
static int pt_pip_load_self_test_param_(struct pt_core_data *cd,
u8 self_test_id, u16 load_offset, u16 load_length,
u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
int rc = 0;
int i;
u8 write_buf[PT_MAX_PIP1_MSG_SIZE];
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_LOAD_SELF_TEST_PARAM),
.write_length = 5 + load_length,
.write_buf = write_buf,
.timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT,
};
write_buf[0] = LOW_BYTE(load_offset);
write_buf[1] = HI_BYTE(load_offset);
write_buf[2] = LOW_BYTE(load_length);
write_buf[3] = HI_BYTE(load_length);
write_buf[4] = self_test_id;
for (i = 0; i < load_length; i++)
write_buf[i + 5] = parameters[i];
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (status)
*status = cd->response_buf[5];
if (ret_test_id)
*ret_test_id = cd->response_buf[6];
if (act_load_len)
*act_load_len = get_unaligned_le16(&cd->response_buf[7]);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip_load_self_test_param
*
* SUMMARY: Protected call to pt_pip_load_self_test_param_ within an exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* self_test_id - enumerated test ID for which the parmeters belong
* load_offset - mem offset to where to load parameters
* load_length - length of parameter data to load
* *parameters - pointer to list of parameter data
* *status - pointer to store the response status
* *ret_test_id - pointer to returned test id the paramters were stored
* *act_load_len - pointer to store the actual load length that was writen
******************************************************************************/
static int pt_pip_load_self_test_param(struct pt_core_data *cd,
u8 self_test_id, u16 load_offset, u16 load_length,
u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
load_length, parameters, status, ret_test_id, act_load_len);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_load_self_test_param
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip_load_self_test_param
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* self_test_id - enumerated test ID for which the parmeters belong
* load_offset - mem offset to where to load parameters
* load_length - length of parameter data to load
* *parameters - pointer to list of parameter data
* *status - pointer to store the response status
* *ret_test_id - pointer to returned test id the paramters were stored
* *act_load_len - pointer to store the actual load length that was writen
******************************************************************************/
static int _pt_request_pip_load_self_test_param(struct device *dev,
int protect, u8 self_test_id, u16 load_offset, u16 load_length,
u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_load_self_test_param(cd, self_test_id,
load_offset, load_length, parameters, status, ret_test_id,
act_load_len);
return pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
load_length, parameters, status, ret_test_id, act_load_len);
}
/*******************************************************************************
* FUNCTION: pt_pip_calibrate_ext_
*
* SUMMARY: Send the PIP1 Extended Calibrate command (0x30) to the DUT waiting
* for the response
*
* NOTE: This calibrate command requires the DUT to support PIP version >= 1.10
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *cal_data - pointer to extended calibration data structure
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_pip_calibrate_ext_(struct pt_core_data *cd,
struct pt_cal_ext_data *cal_data, u8 *status)
{
int rc = 0;
int write_length = 4;
u8 write_buf[4];
u16 size = 0;
unsigned short crc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED),
.write_length = write_length,
.write_buf = write_buf,
.timeout_ms = PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT,
};
if (cal_data == NULL)
return -EINVAL;
memcpy(write_buf, cal_data, sizeof(struct pt_cal_ext_data));
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
if (status)
*status = cd->response_buf[5];
/*
* When doing a calibration on a flashless DUT, save CAL data in
* the TTDL cache on any successful calibration
*/
if (*status == 0 && cd->cal_cache_in_host) {
pt_debug(cd->dev, DL_INFO, "%s: Retrieve and Save CAL\n",
__func__);
rc = _pt_manage_local_cal_data(cd->dev, PT_CAL_DATA_SAVE,
&size, &crc);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Error Saving CAL rc=%d\n", __func__, rc);
else
pt_debug(cd->dev, DL_INFO,
"%s: Saved CAL: chip ID=0x%04X size=%d\n",
__func__, crc, size);
}
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_calibrate_ext
*
* SUMMARY: Protected call to pt_pip_calibrate_ext_ by exclusive access to the
* DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *cal_data - pointer to extended calibration data structure
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_pip_calibrate_ext(struct pt_core_data *cd,
struct pt_cal_ext_data *cal_data, u8 *status)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_calibrate_ext_(cd, cal_data, status);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_calibrate_ext
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_calibrate_ext
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* *cal_data - pointer to extended calibration data structure
* *status - pointer to where the command response status is stored
******************************************************************************/
static int _pt_request_pip_calibrate_ext(struct device *dev,
int protect, struct pt_cal_ext_data *cal_data, u8 *status)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_calibrate_ext(cd, cal_data, status);
return pt_pip_calibrate_ext_(cd, cal_data, status);
}
/*******************************************************************************
* FUNCTION: pt_pip_calibrate_idacs_
*
* SUMMARY: Send the PIP Calibrate IDACs command (0x28) to the DUT waiting
* for the response
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* mode - sense mode to calibrate (0-5)
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_pip_calibrate_idacs_(struct pt_core_data *cd,
u8 mode, u8 *status)
{
int rc = 0;
int write_length = 1;
u8 write_buf[1];
u8 cmd_offset = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_IDACS),
.write_length = write_length,
.write_buf = write_buf,
.timeout_ms = PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT,
};
write_buf[cmd_offset++] = mode;
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
*status = cd->response_buf[5];
if (*status)
return -EINVAL;
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip_calibrate_idacs
*
* SUMMARY: Protected call to pt_hid_output_calibrate_idacs_ by exclusive
* access to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* mode - sense mode to calibrate (0-5)
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_pip_calibrate_idacs(struct pt_core_data *cd,
u8 mode, u8 *status)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip_calibrate_idacs_(cd, mode, status);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_calibrate_idacs
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to pt_pip_calibrate_idacs
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* mode - sense mode to calibrate (0-5)
* *status - pointer to where the command response status is stored
******************************************************************************/
static int _pt_request_pip_calibrate_idacs(struct device *dev,
int protect, u8 mode, u8 *status)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip_calibrate_idacs(cd, mode, status);
return pt_pip_calibrate_idacs_(cd, mode, status);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_initialize_baselines_
*
* SUMMARY: Send the PIP "Initialize Baselines" command (0x29) to the DUT
* waiting for the response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* test_id - bit type flag to allow initialize baseline MUT,BTN,SELG
* each or together with a single command.
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_hid_output_initialize_baselines_(
struct pt_core_data *cd, u8 test_id, u8 *status)
{
int rc = 0;
int write_length = 1;
u8 write_buf[1];
u8 cmd_offset = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_INITIALIZE_BASELINES),
.write_length = write_length,
.write_buf = write_buf,
};
write_buf[cmd_offset++] = test_id;
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
*status = cd->response_buf[5];
if (*status)
return -EINVAL;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_initialize_baselines
*
* SUMMARY: Protected call to pt_hid_output_initialize_baselines_ by exclusive
* access to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* test_id - enumerated ID against which to initialize the baseline
* *status - pointer to where the command response status is stored
******************************************************************************/
static int pt_hid_output_initialize_baselines(struct pt_core_data *cd,
u8 test_id, u8 *status)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_initialize_baselines_(cd, test_id, status);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_initialize_baselines
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip_initialize_baselines
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* test_id - enumerated ID against which to initialize the baseline
* *status - pointer to where the command response status is stored
******************************************************************************/
static int _pt_request_pip_initialize_baselines(struct device *dev,
int protect, u8 test_id, u8 *status)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_initialize_baselines(cd, test_id,
status);
return pt_hid_output_initialize_baselines_(cd, test_id, status);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_exec_panel_scan_
*
* SUMMARY: Sends the PIP "Execute Panel Scan" (0x2A) to the DUT and waits for
* the response
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_output_exec_panel_scan_(struct pt_core_data *cd)
{
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_EXEC_PANEL_SCAN),
};
return pt_pip1_send_and_wait_(cd, &pip1_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_exec_panel_scan
*
* SUMMARY: Protected call to pt_hid_output_exec_panel_scan_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_output_exec_panel_scan(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_exec_panel_scan_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_exec_panel_scan_
*
* SUMMARY: Send the PIP2 "Execute Panel Scan" (0x21) to the DUT and waits for
* the response
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* scan_type - type of panel scan to perform (PIP2 only)
******************************************************************************/
static int pt_pip2_exec_panel_scan_(struct pt_core_data *cd, u8 scan_type)
{
int rc = 0;
u8 data[2];
u8 read_buf[10];
u16 actual_read_len;
pt_debug(cd->dev, DL_DEBUG, "%s: PIP2 Execute Scan %d\n",
__func__, scan_type);
data[0] = scan_type;
rc = _pt_request_pip2_send_cmd(cd->dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_EXECUTE_SCAN,
data, 1, read_buf, &actual_read_len);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s EXECUTE_SCAN command for type %d failed. rc=%d\n",
__func__, scan_type, rc);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_exec_panel_scan
*
* SUMMARY: Protected call to pt_pip2_exec_panel_scan_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* scan_type - type of panel scan to perform (PIP2 only)
******************************************************************************/
static int pt_pip2_exec_panel_scan(struct pt_core_data *cd, u8 scan_type)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip2_exec_panel_scan_(cd, scan_type);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_exec_panel_scan
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_pip2_exec_panel_scan or pt_hid_output_exec_panel_scan
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* scan_type - type of panel scan to perform (PIP2 only)
******************************************************************************/
static int _pt_request_pip_exec_panel_scan(struct device *dev,
int protect, u8 scan_type)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 12)) {
if (protect)
return pt_pip2_exec_panel_scan(cd, scan_type);
return pt_pip2_exec_panel_scan_(cd, scan_type);
}
if (protect)
return pt_hid_output_exec_panel_scan(cd);
return pt_hid_output_exec_panel_scan_(cd);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_retrieve_panel_scan_
*
* SUMMARY: Sends the PIP "Retrieve Panel Scan" (0x2B) command to the DUT
* to retrieve the specified data type for a the last successful Execute
* Panel Scan command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* read_offset - read pointer offset
* read_count - length of data to read
* data_id - enumerated test ID to read selftest results from
* *response - pointer to store the read response status
* *config - pointer to store config data
* *actual_read_len - pointer to store data length actually read
* *read_buf - pointer to the read buffer
******************************************************************************/
static int pt_hid_output_retrieve_panel_scan_(
struct pt_core_data *cd, u16 read_offset, u16 read_count,
u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
u8 *read_buf)
{
int status;
u8 read_data_id;
int rc = 0;
int write_length = 5;
u8 write_buf[5];
u8 cmd_offset = 0;
u8 data_elem_size;
u8 pip3_exofs = 0;
int size;
int data_size;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RETRIEVE_PANEL_SCAN),
.write_length = write_length,
.write_buf = write_buf,
};
write_buf[cmd_offset++] = LOW_BYTE(read_offset);
write_buf[cmd_offset++] = HI_BYTE(read_offset);
write_buf[cmd_offset++] = LOW_BYTE(read_count);
write_buf[cmd_offset++] = HI_BYTE(read_count);
write_buf[cmd_offset++] = data_id;
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
cd->force_pip3_report_type = true;
pip3_exofs = 1;
}
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
status = cd->response_buf[5 - pip3_exofs];
if (status) {
rc = -EINVAL;
goto exit;
}
read_data_id = cd->response_buf[6 - pip3_exofs];
if (read_data_id != data_id) {
rc = -EPROTO;
goto exit;
}
size = get_unaligned_le16(&cd->response_buf[0]);
*actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
*config = cd->response_buf[9];
data_elem_size = *config & 0x07;
data_size = *actual_read_len * data_elem_size;
pt_debug(cd->dev, DL_INFO,
"%s: %s=0x%04X %s=0x%04X %s=0x%02X %s=0x%04X\n",
__func__, "Resp Len", size,
"Actual Read Length", *actual_read_len,
"Element Size", data_elem_size,
"Calc Data Size", data_size);
/* Do not continue if the ARL is larger than the response */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(data_size + 12) != size) {
/*
* For HID/PIP3 response, command 0x2B payload length
* is 12 bytes larger than ARL * Element Size.
*/
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid Actual Read Length\n", __func__);
rc = -EBADMSG;
goto exit;
}
if (read_buf)
memcpy(read_buf, &cd->response_buf[10], data_size);
if (response) {
/* Reconstruct header based on PIP1 response */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
/* Remove 2 bytes CRC */
size -= 2;
cd->response_buf[0] = LOW_BYTE(size);
cd->response_buf[1] = HI_BYTE(size);
cd->response_buf[2] = PT_PIP_NON_HID_RESPONSE_ID;
/* RSVD(0x00) */
cd->response_buf[3] = 0x00;
/* PIP cmd id */
cd->response_buf[4] = 0x2B;
/* Status */
cd->response_buf[5] = 0;
/* Data ID */
cd->response_buf[6] = read_data_id;
}
memcpy(response, cd->response_buf, size);
}
exit:
cd->force_pip3_report_type = false;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_retrieve_panel_scan
*
* SUMMARY: Protected call to pt_hid_output_retrieve_panel_scan_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core_data structure
* read_offset - read pointer offset
* read_count - length of data to read
* data_id - enumerated test ID to read selftest results from
* *response - pointer to store the read response status
* *config - pointer to store config data
* *actual_read_len - pointer to store data length actually read
* *read_buf - pointer to the read buffer
******************************************************************************/
static int pt_hid_output_retrieve_panel_scan(
struct pt_core_data *cd, u16 read_offset, u16 read_count,
u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
u8 *read_buf)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_retrieve_panel_scan_(cd, read_offset,
read_count, data_id, response, config,
actual_read_len, read_buf);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_retrieve_panel_scan
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_hid_output_retrieve_panel_scan
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* read_offset - read pointer offset
* read_count - length of data to read
* data_id - enumerated test ID to read selftest results from
* *response - pointer to store the read response status
* *config - pointer to store config data
* *actual_read_len - pointer to store data length actually read
* *read_buf - pointer to the read buffer
******************************************************************************/
static int _pt_request_pip_retrieve_panel_scan(struct device *dev,
int protect, u16 read_offset, u16 read_count, u8 data_id,
u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_retrieve_panel_scan(cd,
read_offset, read_count, data_id, response,
config, actual_read_len, read_buf);
return pt_hid_output_retrieve_panel_scan_(cd,
read_offset, read_count, data_id, response,
config, actual_read_len, read_buf);
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_user_cmd
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_hid_output_user_cmd
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* read_len - length of data to read
* *read_buf - pointer to store read data
* write_len - length of data to write
* *write_buf - pointer to buffer to write
* *actual_read_len - pointer to store data length actually read
******************************************************************************/
static int _pt_request_pip_user_cmd(struct device *dev,
int protect, u16 read_len, u8 *read_buf, u16 write_len,
u8 *write_buf, u16 *actual_read_len)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_user_cmd(cd, read_len, read_buf,
write_len, write_buf, actual_read_len);
return pt_hid_output_user_cmd_(cd, read_len, read_buf,
write_len, write_buf, actual_read_len);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_get_information_
*
* SUMMARY: Sends the PIP "Get Bootloader Information" (0x38) command to the
* DUT to retrieve bootloader version and chip identification information.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *return_data - pointer to store the return data
*****************************************************************************/
static int pt_hid_output_bl_get_information_(struct pt_core_data *cd,
u8 *return_data)
{
int rc;
int data_len;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_INFO),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
return rc;
data_len = get_unaligned_le16(&cd->input_buf[6]);
if (!data_len)
return -EPROTO;
memcpy(return_data, &cd->response_buf[8], data_len);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_get_information
*
* SUMMARY: Protected call to pt_hid_output_bl_get_information_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *return_data - pointer to store the return data
******************************************************************************/
static int pt_hid_output_bl_get_information(struct pt_core_data *cd,
u8 *return_data)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_get_information_(cd, return_data);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_bl_get_information
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_hid_output_bl_get_information
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* *return_data - pointer to store bl data
******************************************************************************/
static int _pt_request_pip_bl_get_information(struct device *dev,
int protect, u8 *return_data)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_get_information(cd, return_data);
return pt_hid_output_bl_get_information_(cd, return_data);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_initiate_bl_
*
* SUMMARY: Sends the PIP "Initiate Bootload" (0x48) command to the
* DUT to erases the entire TrueTouch application, Configuration Data block,
* and Design Data block in flash and enables the host to execute the Program
* and Verify Row command to bootload the application image and data.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* protect - flag to call protected or non-protected
* key_size - size of key
* *key_buf - pointer to key data to allow operation
* row_size - size of the meta data row
* *metadata_row_buf - pointer to meta data to write
******************************************************************************/
static int pt_hid_output_bl_initiate_bl_(struct pt_core_data *cd,
u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
{
u16 write_length = key_size + row_size;
u8 *write_buf;
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_INITIATE_BL),
.write_length = write_length,
.timeout_ms = PT_PIP1_CMD_INITIATE_BL_TIMEOUT,
};
write_buf = kzalloc(write_length, GFP_KERNEL);
if (!write_buf)
return -ENOMEM;
pip1_cmd.write_buf = write_buf;
if (key_size)
memcpy(write_buf, key_buf, key_size);
if (row_size)
memcpy(&write_buf[key_size], metadata_row_buf, row_size);
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
kfree(write_buf);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_initiate_bl
*
* SUMMARY: Protected call to pt_hid_output_bl_initiate_bl_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* key_size - size of key
* *key_buf - pointer to key data to allow operation
* row_size - size of the meta data row
* *metadata_row_buf - pointer to meta data to write
******************************************************************************/
static int pt_hid_output_bl_initiate_bl(struct pt_core_data *cd,
u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
row_size, metadata_row_buf);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_bl_initiate_bl
*
* SUMMARY: Function pointer included in core_nonhid_cmd struct for external
* calls to the protected or unprotected call to
* pt_hid_output_bl_initiate_bl
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* key_size - size of key
* *key_buf - pointer to key data to allow operation
* row_size - size of the meta data row
* *metadata_row_buf - pointer to meta data to write
******************************************************************************/
static int _pt_request_pip_bl_initiate_bl(struct device *dev,
int protect, u16 key_size, u8 *key_buf, u16 row_size,
u8 *metadata_row_buf)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_initiate_bl(cd, key_size, key_buf,
row_size, metadata_row_buf);
return pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
row_size, metadata_row_buf);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_program_and_verify_
*
* SUMMARY: Sends the PIP "Program and Verify" (0x39) command to upload
* and program a 128-byte row into the flash, and then verifies written data.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* data_len - length of data_buf
* *data_buf - firmware image to program
******************************************************************************/
static int pt_hid_output_bl_program_and_verify_(
struct pt_core_data *cd, u16 data_len, u8 *data_buf)
{
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY),
.write_length = data_len,
.write_buf = data_buf,
.timeout_ms = PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT,
};
return pt_pip1_send_and_wait_(cd, &pip1_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_program_and_verify
*
* SUMMARY: Protected call to pt_hid_output_bl_program_and_verify_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* data_len - length of data_buf
* *data_buf - firmware image to program
******************************************************************************/
static int pt_hid_output_bl_program_and_verify(
struct pt_core_data *cd, u16 data_len, u8 *data_buf)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_program_and_verify_(cd, data_len, data_buf);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_bl_program_and_verify
*
* SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
* to request to have the BL program and verify a FW image
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - boolean to determine to call the protected function
* data_len - length of data_buf
* *data_buf - firmware image to program
******************************************************************************/
static int _pt_request_pip_bl_program_and_verify(
struct device *dev, int protect, u16 data_len, u8 *data_buf)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_program_and_verify(cd, data_len,
data_buf);
return pt_hid_output_bl_program_and_verify_(cd, data_len,
data_buf);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_verify_app_integrity_
*
* SUMMARY: Sends the PIP "Verify Application Integrity" (0x31) command to
* perform a full verification of the application integrity by calculating the
* CRC of the image in flash and compare it to the expected CRC stored in the
* Metadata row.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *result - pointer to store result
******************************************************************************/
static int pt_hid_output_bl_verify_app_integrity_(
struct pt_core_data *cd, u8 *result)
{
int rc;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc) {
*result = 0;
return rc;
}
*result = cd->response_buf[8];
return 0;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_verify_app_integrity
*
* SUMMARY: Protected call to pt_hid_output_bl_verify_app_integrity_ by
* exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *result - pointer to store result
******************************************************************************/
static int pt_hid_output_bl_verify_app_integrity(
struct pt_core_data *cd, u8 *result)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_verify_app_integrity_(cd, result);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_bl_verify_app_integrity
*
* SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
* to request to have the BL verify the application integrity (PIP1.x only)
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - boolean to determine to call the protected function
* *result - pointer to store result
******************************************************************************/
static int _pt_request_pip_bl_verify_app_integrity(
struct device *dev, int protect, u8 *result)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_verify_app_integrity(cd, result);
return pt_hid_output_bl_verify_app_integrity_(cd, result);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_launch_app_
*
* SUMMARY: Sends the PIP "Launch Application" (0x3B) command to launch the
* application from bootloader (PIP1.x only).
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_output_bl_launch_app_(struct pt_core_data *cd)
{
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_LAUNCH_APP),
.reset_expected = 1,
};
return pt_pip1_send_and_wait_(cd, &pip1_cmd);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_launch_app
*
* SUMMARY: Protected call to pt_hid_output_bl_launch_app_ by exclusive access
* to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_hid_output_bl_launch_app(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_launch_app_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_launch_app
*
* SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
* to request to have the BL launch the application. (PIP1.x only)
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - boolean to determine to call the protected function
******************************************************************************/
static int _pt_request_pip_launch_app(struct device *dev,
int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_launch_app(cd);
return pt_hid_output_bl_launch_app_(cd);
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_get_panel_id_
*
* SUMMARY: Sends the PIP "Get Panel ID" (0x3E) command to return the Panel ID
* value store in the System Information.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *panel_id - pointer to where the panel ID will be stored
******************************************************************************/
static int pt_hid_output_bl_get_panel_id_(
struct pt_core_data *cd, u8 *panel_id)
{
int rc;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_PANEL_ID),
};
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) {
pt_debug(cd->dev, DL_ERROR,
"%s: Get Panel ID command not supported\n",
__func__);
*panel_id = PANEL_ID_NOT_ENABLED;
return 0;
} else if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on Get Panel ID command\n", __func__);
return rc;
}
*panel_id = cd->response_buf[8];
return 0;
}
/*******************************************************************************
* FUNCTION: pt_hid_output_bl_get_panel_id
*
* SUMMARY: Protected call to pt_hid_output_bl_get_panel_id_ by exclusive access
* to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *panel_id - pointer to where the panel ID will be stored
******************************************************************************/
static int pt_hid_output_bl_get_panel_id(
struct pt_core_data *cd, u8 *panel_id)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_hid_output_bl_get_panel_id_(cd, panel_id);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip_bl_get_panel_id
*
* SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
* to have the BL retrieve the panel ID
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to run in protected mode
* *panel_id - pointer to where the panel ID will be stored
******************************************************************************/
static int _pt_request_pip_bl_get_panel_id(
struct device *dev, int protect, u8 *panel_id)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_hid_output_bl_get_panel_id(cd, panel_id);
return pt_hid_output_bl_get_panel_id_(cd, panel_id);
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_status_
*
* SUMMARY: Sends a PIP2 STATUS command to the DUT and stores the data in
* cd status_data.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip2_get_status_(struct pt_core_data *cd)
{
u16 actual_read_len;
u8 read_buf[12];
u8 status, boot;
int rc = 0;
rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len);
if (rc) {
pt_debug(cd->dev, DL_ERROR, "%s: PIP2 STATUS command rc = %d\n",
__func__, rc);
return rc;
}
pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len,
"PIP2 STATUS");
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (status == PIP2_RSP_ERR_INIT_FAILURE) {
pt_debug(cd->dev, DL_ERROR,
"%s: PIP2 Initialization Failure, FW stuck in BOOTUP SysMode. status code = %d\n",
__func__, status);
rc = status;
}
boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
cd->dut_status.fw_system_mode =
read_buf[PIP2_RESP_BODY_OFFSET + 1];
if (status == PIP2_RSP_ERR_NONE && boot == 0x00)
cd->dut_status.mode = PT_MODE_BOOTLOADER;
else if ((status == PIP2_RSP_ERR_NONE ||
status == PIP2_RSP_ERR_INIT_FAILURE) &&
boot == 0x01) {
cd->dut_status.mode = PT_MODE_OPERATIONAL;
/* protocol mode need a mask: 0x07 */
cd->dut_status.protocol_mode =
read_buf[PIP2_RESP_BODY_OFFSET + 2] & 0x07;
if (cd->dut_status.fw_system_mode ==
FW_SYS_MODE_SECONDARY_IMAGE)
cd->dut_status.mode = PT_MODE_SECONDARY_IMAGE;
} else
cd->dut_status.mode = PT_MODE_UNKNOWN;
if (cd->set_protocol_mode) {
if (cd->dut_status.protocol_mode != cd->protocol_mode)
pt_debug(cd->dev, DL_WARN,
"%s: ATM - protocol mode mismatch! dut_status.protocol_mode=%d cd->protocol_mode=%d\n",
__func__, cd->dut_status.protocol_mode,
cd->protocol_mode);
} else
cd->protocol_mode = cd->dut_status.protocol_mode;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_mode_sysmode_
*
* SUMMARY: Determine the current mode and system mode of the DUT by use of the
* PIP2 STATUS command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* *mode - pointer to store the retrieved mode
* *sys_mode - pointer to store the FW system mode
******************************************************************************/
static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd,
u8 *mode, u8 *sys_mode)
{
int rc = 0;
rc = pt_pip2_get_status_(cd);
if (!rc || (rc == PIP2_RSP_ERR_INIT_FAILURE)) {
if (sys_mode)
*sys_mode = cd->dut_status.fw_system_mode;
if (mode)
*mode = cd->dut_status.mode;
} else {
if (mode)
*mode = PT_MODE_UNKNOWN;
if (sys_mode)
*sys_mode = FW_SYS_MODE_UNDEFINED;
pt_debug(cd->dev, DL_WARN,
"%s: Mode and sys_mode could not be determined\n",
__func__);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_mode_sysmode
*
* SUMMARY: Protected call to pt_pip2_get_mode_sysmode_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* *mode - pointer to store the retrieved mode
* *sys_mode - pointer to store the FW system mode
******************************************************************************/
static int pt_pip2_get_mode_sysmode(struct pt_core_data *cd,
u8 *mode, u8 *sys_mode)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip2_get_mode_sysmode
*
* SUMMARY: Function pointer included in core_commands struct for external
* calls to the protected or unprotected call to
* pt_pip2_get_mode_sysmode
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *mode - pointer to store the retrieved mode
* *sys_mode - pointer to store the FW system mode
******************************************************************************/
static int _pt_request_pip2_get_mode_sysmode(struct device *dev,
int protect, u8 *mode, u8 *sys_mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_pip2_get_mode_sysmode(cd, mode, sys_mode);
return pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);
}
/*******************************************************************************
* FUNCTION: _pt_poll_for_fw_exit_boot_mode
*
* SUMMARY: Verify and or poll for the FW to exit BOOT mode. During the FW BOOT
* mode only the following PIP commands will be serviced, any other PIP
* command the FW will respond with an "Invalid PIP Command" response.
* - Get HID Descriptor (Register 0x0001, no command ID)
* - Reset (Register 0x0005, RESET HID request)
* - Ping (Register 0x0004, Command ID 0x00
* - Get System Information (Register 0x0004, Command ID 0x02)
* - PIP2 Status (Register 0x0101, Command ID 0x01)
* - PIP2 Version (Register 0x0101, Command ID 0x07)
* This function will loop on the results of the STATUS command until
* the FW reports it is out of BOOT mode.
*
* NOTE:
* - This function will update cd->fw_system_mode
* - The STATUS cmd only supports this functionality for PIP 1.11+
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* timeout - max time (ms) to wait for FW to exit BOOT mode
* actual_wait - pointer to actual time waited for FW to exit BOOT mode
******************************************************************************/
static int _pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
int *actual_wait)
{
int loop = 0;
u8 sys_mode = cd->fw_system_mode;
u8 pause = 10; /* in ms */
int rc = 0;
int max_loop = (timeout / pause) + 1; /* Add 1 due to int math */
if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
/*
* For PIP <1.11, no support for polling wait so do a hard
* coded wait and assume the FW is out of BOOT. Added 1 to
* timeout to make it clear in kmsg if non polling was done.
*/
*actual_wait = PT_FW_EXIT_BOOT_MODE_TIMEOUT + 1;
pt_debug(cd->dev, DL_ERROR,
"%s: PIP %d.%d no support for ext STATUS, sleep %d\n",
__func__,
cd->sysinfo.ttdata.pip_ver_major,
cd->sysinfo.ttdata.pip_ver_minor, *actual_wait);
msleep(*actual_wait);
sys_mode = FW_SYS_MODE_SCANNING;
}
if (sys_mode == FW_SYS_MODE_BOOT) {
while (!rc && loop <= max_loop &&
(sys_mode == FW_SYS_MODE_BOOT)) {
loop++;
usleep_range(9000, pause * 1000);
rc = pt_pip2_get_mode_sysmode_(cd, NULL, &sys_mode);
pt_debug(cd->dev, DL_DEBUG,
"%s: FW in BOOT mode-sleep %dms, sys_mode=%d\n",
__func__, loop * pause, sys_mode);
}
*actual_wait = (int)(loop * pause);
pt_debug(cd->dev, DL_WARN,
"%s: FW exited BOOT mode in %dms, sys_mode=%d\n",
__func__, *actual_wait, sys_mode);
if (rc)
sys_mode = FW_SYS_MODE_UNDEFINED;
else if (sys_mode == FW_SYS_MODE_BOOT ||
sys_mode == FW_SYS_MODE_UNDEFINED)
rc = -EBUSY;
}
mutex_lock(&cd->system_lock);
cd->fw_system_mode = sys_mode;
mutex_unlock(&cd->system_lock);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_poll_for_fw_exit_boot_mode
*
* SUMMARY: Protected call to _pt_poll_for_fw_exit_boot_mode by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* timeout - max time (ms) to wait for FW to exit BOOT mode
* actual_wait - pointer to actual time waited for FW to exit BOOT mode
******************************************************************************/
static int pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
int *actual_wait)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = _pt_poll_for_fw_exit_boot_mode(cd, timeout, actual_wait);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_get_fw_sys_mode
*
* SUMMARY: Determine the FW system mode. For PIP 1.11+ the
* PIP2 STATUS command is used to directly query the FW system mode. For older
* PIP versions, there is no direct PIP commamnd that will directly provide this
* information but any PIP command above 0x1F requires scanning to be disabled
* before it will be operational. If scanning was not disabled before sending
* these PIP commands the FW will respond with a 6 byte error response. So to
* safely determine the scanning state, a PIP message that will not affect the
* operation of the FW was chosen:
* "Verify Data Block CRC (ID 0x20)" is sent and if a 6 byte error code is
* received scanning is active.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *sys_mode - pointer to FW System mode
* *mode - pointer to mode (BL/FW)
******************************************************************************/
static int _pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
{
int write_length = 1;
int report_length;
int rc = 0;
u8 tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
u8 tmp_mode = PT_MODE_UNKNOWN;
u8 param[1] = { PT_TCH_PARM_EBID };
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
.write_length = write_length,
.write_buf = param,
.novalidate = true,
};
/* AFter PIP1.11 the preferred method is using STATUS cmd */
if (IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
rc = pt_pip2_get_mode_sysmode_(cd, &tmp_mode, &tmp_sys_mode);
pt_debug(cd->dev, DL_DEBUG, "%s: tmp_sys_mode=%d tmp_mode=%d\n",
__func__, tmp_sys_mode, tmp_mode);
if (!rc) {
if (tmp_mode != PT_MODE_OPERATIONAL)
tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
}
goto exit;
}
/* Older systems use PIP1 CONFIG_BLOCK_CRC to best determine sys_mode */
if (cd->mode != PT_MODE_OPERATIONAL) {
tmp_mode = cd->mode;
goto exit;
}
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (rc)
goto exit;
report_length = (cd->response_buf[1] << 8) | (cd->response_buf[0]);
if ((report_length == 0x06) &&
((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == 0x00) &&
(cd->response_buf[5] == PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
tmp_mode = PIP2_STATUS_APP_EXEC;
tmp_sys_mode = FW_SYS_MODE_SCANNING;
} else if ((report_length == 0x0A) &&
((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) ==
PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
tmp_mode = PIP2_STATUS_APP_EXEC;
tmp_sys_mode = FW_SYS_MODE_TEST;
}
exit:
if (mode)
*mode = tmp_mode;
if (sys_mode)
*sys_mode = tmp_sys_mode;
pt_debug(cd->dev, DL_INFO, "%s: Return Mode=%d sys_mode=%d\n",
__func__, tmp_mode, tmp_sys_mode);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_get_fw_sys_mode
*
* SUMMARY: Protected call to _pt_get_fw_sys_mode() to determine if FW scanning
* is active or not.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *sys_mode - pointer to fw system mode
* *mode - pointer to mode
******************************************************************************/
static int pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = _pt_get_fw_sys_mode(cd, sys_mode, mode);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_get_fw_sys_mode
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request to get scan state
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
* *sys_mode - pointer to FW system mode
* *mode - pointer to mode
******************************************************************************/
static int _pt_request_get_fw_sys_mode(struct device *dev, int protect,
u8 *sys_mode, u8 *mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
if (protect)
return pt_get_fw_sys_mode(cd, sys_mode, mode);
return _pt_get_fw_sys_mode(cd, sys_mode, mode);
}
/* Default hid descriptor to provide basic register map */
const struct pt_hid_desc hid_desc_default = {
230, /* hid_desc_len */
HID_APP_REPORT_ID, /* packet_id */
0x00, /* reserved_byte */
0x0100, /* bcd_version */
0x00EC, /* report_desc_len */
0x0002, /* report_desc_register */
0x0003, /* input_register */
0x00FE, /* max_input_len */
0x0004, /* output_register */
0x00FF, /* max_output_len */
0x0005, /* command_register */
0x0006, /* data_register */
0x04B4, /* vendor_id */
0xC101, /* product_id */
0x0100, /* version_id */
{0x00, 0x00, 0x00, 0x00} /* reserved[4] */
};
/*******************************************************************************
* FUNCTION: pt_init_hid_descriptor
*
* SUMMARY: Setup default values for HID descriptor structure
*
*
* PARAMETERS:
* *desc - pointer to the HID descriptor data read back from DUT
******************************************************************************/
static inline void pt_init_hid_descriptor(struct pt_hid_desc *desc)
{
memcpy(desc, &hid_desc_default, sizeof(hid_desc_default));
}
/*******************************************************************************
* FUNCTION: pt_get_hid_descriptor_
*
* SUMMARY: Send the get HID descriptor command to the DUT and load the response
* into the HID descriptor structure
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *desc - pointer to the HID descriptor data read back from DUT
******************************************************************************/
static int pt_get_hid_descriptor_(struct pt_core_data *cd,
struct pt_hid_desc *desc)
{
struct device *dev = cd->dev;
struct pt_hid_cmd hid_cmd = {
.descriptor = cd->hid_core.hid_desc_register,
.read_length = 0,
};
int rc = 0;
u16 hid_len;
/*
* During startup the HID descriptor is required for all future
* processing. If IRQ is already asserted due to an early touch report
* the report must be cleared before sending command.
*/
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
rc = pt_hid_send_command_(cd, &hid_cmd);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: failed to get HID descriptor, rc=%d\n",
__func__, rc);
goto exit;
}
hid_len = get_unaligned_le16(&cd->response_buf[0]);
pt_pr_buf(cd->dev, DL_DEBUG, cd->response_buf, hid_len, "<<< HIDDesc");
/*
* HID doesn't have packet_id and reserve_byte in struct struct
* pt_hid_desc and assign fixed value to packet_id.
*/
if (cd->protocol_mode != PT_PROTOCOL_MODE_PIP) {
if ((hid_len + 2) != sizeof(*desc)) {
pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n",
__func__, hid_len);
rc = -ENODEV;
goto exit;
}
/* Copy length field */
memcpy((u8 *)desc, &cd->response_buf[0], 2);
/* Assign fixed value to packet_id */
desc->packet_id = HID_APP_REPORT_ID;
/* Skip 2 bytes in desc and Copy to left fields */
memcpy((u8 *)desc + 4, &cd->response_buf[2], hid_len - 2);
} else {
if (hid_len != sizeof(*desc)) {
pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n",
__func__, hid_len);
rc = -ENODEV;
goto exit;
}
/* Load the HID descriptor including all registers */
memcpy((u8 *)desc, cd->response_buf, hid_len);
}
/* Check HID descriptor length and version */
pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__,
le16_to_cpu(desc->hid_desc_len),
le16_to_cpu(desc->bcd_version));
cd->hid_core.hid_report_desc_len =
le16_to_cpu(desc->report_desc_len);
cd->hid_core.hid_max_input_len =
le16_to_cpu(desc->max_input_len);
cd->hid_core.hid_max_output_len =
le16_to_cpu(desc->max_output_len);
cd->hid_core.hid_protocol_ver =
(desc->version_id & 0xff00) >> 8;
pt_debug(dev, DL_INFO,
"%s: report desc len:%d, max_input_len:%d, max_output_len:%d\n",
__func__,
cd->hid_core.hid_report_desc_len,
cd->hid_core.hid_max_input_len,
cd->hid_core.hid_max_output_len);
if (le16_to_cpu(desc->bcd_version) != HID_VERSION) {
pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n",
__func__);
rc = -ENODEV;
goto exit;
}
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, cd->response_buf, hid_len, "HIDDesc=");
#endif
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_get_hid_descriptor
*
* SUMMARY: Protected call to pt_get_hid_descriptor_()
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *desc - pointer to the HID descriptor data read back from DUT
******************************************************************************/
static int pt_get_hid_descriptor(struct pt_core_data *cd,
struct pt_hid_desc *desc)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_get_hid_descriptor_(cd, desc);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_version_
*
* SUMMARY: Sends a PIP2 VERSION command to the DUT and stores the data in
* cd-ttdata
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_pip2_get_version_(struct pt_core_data *cd)
{
int rc = 0;
int status;
u8 read_buf[64];
u16 actual_read_len;
rc = _pt_request_pip2_send_cmd(cd->dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
NULL, 0, read_buf, &actual_read_len);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error Sending PIP2 VERSION Cmd rc=%d\n",
__func__, rc);
return rc;
}
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (status == 0) {
/* Parse the PIP2 VERSION response into ttdata */
pt_pip2_ver_load_ttdata(cd, actual_read_len);
} else {
pt_debug(cd->dev, DL_ERROR,
"%s: Error in PIP2 VERSION Cmd status=%d\n",
__func__, status);
return status;
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_get_version
*
* SUMMARY: Protected call to pt_pip2_get_version_ by exclusive
* access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_pip2_get_version(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip2_get_version_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_active_pip_protocol
*
* SUMMARY: Get active PIP protocol version using the PIP2 version command.
* Function will return PIP version of BL or application based on
* when it's called.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to run in protected mode
* *pip_version_major - pointer to store PIP major version
* *pip_version_minor - pointer to store PIP minor version
******************************************************************************/
int _pt_request_active_pip_protocol(struct device *dev, int protect,
u8 *pip_version_major, u8 *pip_version_minor)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
int rc = 0;
struct pt_pip1_cmd pip1_cmd = {
CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
};
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
/* Skip PIP2 command if DUT generation is confirmed */
if (cd->active_dut_generation == DUT_PIP1_ONLY)
goto skip_pip2_command;
rc = pt_pip2_get_version_(cd);
if (!rc) {
*pip_version_major = ttdata->pip_ver_major;
*pip_version_minor = ttdata->pip_ver_minor;
pt_debug(dev, DL_INFO,
"%s: pip_version = %d.%d\n", __func__,
*pip_version_major, *pip_version_minor);
} else {
/*
* Legacy products do not support the pip2 protocol to get
* pip version. However, they do support the "get sysinfo"
* command to get pip version from FW, but the bootloader
* does not support it. This function will try "get sysinfo"
* command if the pip2 command failed but this cmd could also
* fail if DUT is stuck in bootloader mode.
*/
pt_debug(dev, DL_INFO,
"%s: PIP2 no response rc = %d, try legacy cmd\n",
__func__, rc);
skip_pip2_command:
rc = pt_pip1_send_and_wait_(cd, &pip1_cmd);
if (!rc) {
*pip_version_minor =
cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET + 1];
*pip_version_major =
cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];
pt_debug(dev, DL_INFO,
"%s: pip_version = %d.%d\n", __func__,
*pip_version_major, *pip_version_minor);
} else {
*pip_version_major = 0;
*pip_version_minor = 0;
pt_debug(dev, DL_ERROR,
"%s: pip_version Not Detected\n", __func__);
}
}
return rc;
}
EXPORT_SYMBOL_GPL(_pt_request_active_pip_protocol);
/*******************************************************************************
* FUNCTION: _pt_detect_dut_by_pip1
*
* SUMMARY: Detect DUT by PIP1 HID_DESC command.
* Packet_ID in BL = HID_BL_REPORT_ID (0xFF)
* Packet_ID in FW = HID_APP_REPORT_ID (0xF7)
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *status - pointer to status bitmask
* *dut_gen - pointer to store the dut_generation
* *mode - pointer to store the pt_mode
* *prot_mode - pointer to store the protocol_mode
******************************************************************************/
static int _pt_detect_dut_by_pip1(struct device *dev,
u32 *enum_status, u8 *dut_gen, enum pt_mode *mode,
u8 *prot_mode)
{
int rc = 0;
u8 index = 0;
u8 dut_gen_tmp = DUT_UNKNOWN;
u8 mode_tmp = PT_MODE_UNKNOWN;
u8 prot_mode_tmp = PT_PROTOCOL_MODE_UNKNOWN;
u8 write_buf[2];
u8 read_buf[PT_MAX_PIP1_MSG_SIZE];
u16 actual_read_len;
u32 enum_status_tmp = ENUM_STATUS_START;
struct pt_core_data *cd = dev_get_drvdata(dev);
/* Flush Bus to clear pending report */
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
/* Suppose to be PIP1 BL/Operation and detect it by report decriptor. */
memset(write_buf, 0, ARRAY_SIZE(write_buf));
memset(read_buf, 0, ARRAY_SIZE(read_buf));
write_buf[index++] = 0x01;
write_buf[index++] = 0x00;
rc = pt_hid_output_user_cmd_(cd, PT_MAX_PIP1_MSG_SIZE, read_buf, index,
write_buf, &actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to get PIP1 hid descriptor, rc=%d\n",
__func__, rc);
/* Flush Bus to clear pending report */
pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
goto exit;
}
if (read_buf[PIP1_RESP_REPORT_ID_OFFSET] == HID_BL_REPORT_ID) {
dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 BL */
mode_tmp = PT_MODE_BOOTLOADER;
prot_mode_tmp = PT_PROTOCOL_MODE_PIP;
enum_status_tmp = ENUM_STATUS_BL_RESET_SENTINEL;
} else if (read_buf[PIP1_RESP_REPORT_ID_OFFSET] ==
HID_APP_REPORT_ID) {
dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 FW */
mode_tmp = PT_MODE_OPERATIONAL;
prot_mode_tmp = PT_PROTOCOL_MODE_PIP;
enum_status_tmp = ENUM_STATUS_FW_RESET_SENTINEL;
} else {
pt_debug(dev, DL_INFO, "%s:Invalid report ID=%d\n", __func__,
read_buf[PIP1_RESP_REPORT_ID_OFFSET]);
rc = -EINVAL;
}
exit:
mutex_lock(&cd->system_lock);
if (dut_gen)
*dut_gen = dut_gen_tmp;
if (mode)
*mode = mode_tmp;
if (prot_mode)
*prot_mode = prot_mode_tmp;
if (enum_status)
*enum_status = enum_status_tmp;
mutex_unlock(&cd->system_lock);
#ifdef TTDL_DIAGNOSTICS
pt_debug(dev, DL_INFO, "%s: Generation=%d Mode=%d Prot_mode=%d\n",
__func__, dut_gen_tmp, mode_tmp, prot_mode_tmp);
#endif /* TTDL_DIAGNOSTICS */
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_detect_dut_by_pip2
*
* SUMMARY: Detect DUT by use of the PIP2 STATUS command. If the DUT responds
* correctly the DUT generation is known however the protocol mode is only
* known if the FW application responds because the ROM is shared with DUTs
* that use different application protocols.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *status - pointer to status bitmask
* *dut_gen - pointer to store the dut_generation
* *mode - pointer to store the pt_mode
* *prot_mode - pointer to store the protocol_mode
******************************************************************************/
static int _pt_detect_dut_by_pip2(struct device *dev,
u32 *enum_status, u8 *dut_gen, enum pt_mode *mode,
u8 *prot_mode)
{
int rc = 0;
u8 index = 0;
u8 dut_gen_tmp = DUT_UNKNOWN;
u8 mode_tmp = PT_MODE_UNKNOWN;
u8 prot_mode_tmp = PT_PROTOCOL_MODE_UNKNOWN;
u8 write_buf[8];
u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
u8 status, boot;
u32 enum_status_tmp = ENUM_STATUS_START;
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_pip2_cmd pip2_cmd = {
.cmd_id = PIP2_CMD_ID_STATUS,
.cmd_tag_seq = 0x08,
.read_buf = read_buf,
};
/* Flush Bus to clear pending report */
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
/*
* Suppose to be PIP2_CABABLE/BL-ROM/HYBRIDE.
* NOTE: If BL-ROM, DUT_GEN can't determined.
*/
memset(write_buf, 0, ARRAY_SIZE(write_buf));
memset(read_buf, 0, ARRAY_SIZE(read_buf));
write_buf[index++] = 0x01; /* LSB, PIP2 CMD Register */
write_buf[index++] = 0x01; /* MSB, PIP2 CMD Register */
write_buf[index++] = 0x06; /* LSB, Length */
write_buf[index++] = 0x00; /* MSB, Length */
write_buf[index++] = 0x08; /* SEQ */
write_buf[index++] = PIP2_CMD_ID_STATUS; /* CMD_ID */
write_buf[index++] = 0x3A; /* MSB, CRC */
write_buf[index++] = 0xD1; /* LSB, CRC */
/*
* TBD: Below function will be replaced with pt_pip2_send_and_wait_() or
* equalling one after protocol mode switch is removed from lower level.
*/
rc = pt_hid_output_user_cmd_(cd, PT_MAX_PIP2_MSG_SIZE, read_buf, index,
write_buf, &pip2_cmd.actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to send PIP2 STATUS, rc=%d\n", __func__,
rc);
goto exit;
}
/* Validate STATUS response */
rc = pt_pip2_validate_response(cd, &pip2_cmd);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to validate PIP2 STATUS, rc=%d\n",
__func__, rc);
goto exit;
}
status = read_buf[PIP2_RESP_STATUS_OFFSET];
boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
if (status == PIP2_RSP_ERR_INIT_FAILURE) {
dut_gen_tmp = DUT_PIP2_CAPABLE;
mode_tmp = PT_MODE_OPERATIONAL;
prot_mode_tmp = PT_PROTOCOL_MODE_PIP;
enum_status_tmp = ENUM_STATUS_FW_RESET_SENTINEL;
} else if (status == PIP2_RSP_ERR_NONE) {
if (boot == 0x00) {
dut_gen_tmp = DUT_PIP2_CAPABLE;
mode_tmp = PT_MODE_BOOTLOADER;
prot_mode_tmp = PT_PROTOCOL_MODE_PIP;
enum_status_tmp = ENUM_STATUS_BL_RESET_SENTINEL;
} else {
mode_tmp = PT_MODE_OPERATIONAL;
/* protocol mode need a mask: 0x07 */
prot_mode_tmp =
read_buf[PIP2_RESP_BODY_OFFSET + 2] & 0x07;
dut_gen_tmp = DUT_PIP2_CAPABLE;
enum_status_tmp = ENUM_STATUS_FW_RESET_SENTINEL;
}
}
exit:
mutex_lock(&cd->system_lock);
if (dut_gen)
*dut_gen = dut_gen_tmp;
if (mode)
*mode = mode_tmp;
if (prot_mode)
*prot_mode = prot_mode_tmp;
if (enum_status)
*enum_status = enum_status_tmp;
mutex_unlock(&cd->system_lock);
#ifdef TTDL_DIAGNOSTICS
pt_debug(dev, DL_INFO, "%s: Generation=%d Mode=%d Prot_mode=%d\n",
__func__, dut_gen_tmp, mode_tmp, prot_mode_tmp);
#endif /* TTDL_DIAGNOSTICS */
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_detect_dut_by_pip3
*
* SUMMARY: Detect DUT by PIP3 STATUS command. If responsed correctly, the
* protocol mode known as HID and mode could be Operation or Secondary Image.
*
* NOTE: PIP3 device belongs to DUT_PIP2_CAPABLE generation temporarily.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *status - pointer to status bitmask
* *dut_gen - pointer to store the dut_generation
* *mode - pointer to store the pt_mode
* *prot_mode - pointer to store the protocol_mode
******************************************************************************/
static int _pt_detect_dut_by_pip3(struct device *dev,
u32 *enum_status, u8 *dut_gen, enum pt_mode *mode,
u8 *prot_mode)
{
int rc = 0;
u8 index = 0;
u8 dut_gen_tmp = DUT_UNKNOWN;
u8 mode_tmp = PT_MODE_UNKNOWN;
u8 prot_mode_tmp = PT_PROTOCOL_MODE_UNKNOWN;
u8 write_buf[7];
u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
u8 status, boot;
u16 actual_read_len = 0;
u32 enum_status_tmp = ENUM_STATUS_START;
struct pt_core_data *cd = dev_get_drvdata(dev);
/* Flush Bus to clear pending report */
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
/* Suppose to be PIP3. */
memset(write_buf, 0, ARRAY_SIZE(write_buf));
memset(read_buf, 0, ARRAY_SIZE(read_buf));
write_buf[index++] = 0x04; /* report ID */
write_buf[index++] = 0x06; /* LSB, Length */
write_buf[index++] = 0x00; /* MSB, Length */
write_buf[index++] = 0x08; /* SEQ */
write_buf[index++] = HID_CMD_ID_STATUS; /* CMD_ID */
write_buf[index++] = 0x3A; /* MSB, CRC */
write_buf[index++] = 0xD1; /* LSB, CRC */
/* Set report type to PIP3 */
cd->pt_hid_buf_op.report_type = PIP3_CMD_REPORT;
rc = pt_hid_user_cmd_send_(cd, write_buf, index);
if (rc) {
pt_debug(dev, DL_ERROR, "%s: Failed to send PIP3 STATUS\n",
__func__);
goto exit;
} else {
actual_read_len = get_unaligned_le16(&cd->response_buf[0]);
if (actual_read_len == 0)
actual_read_len = 2;
/* Payload len of STATUS is 11 */
if (actual_read_len != 11) {
pt_debug(dev, DL_ERROR, "%s: Response len(%d) error\n",
__func__, actual_read_len);
rc = -EINVAL;
goto exit;
}
/* Copy the payload to read_buf */
memcpy(read_buf, cd->response_buf, actual_read_len);
}
status = read_buf[PIP2_RESP_STATUS_OFFSET];
boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
if (status == PIP2_RSP_ERR_INIT_FAILURE) {
dut_gen_tmp = DUT_PIP2_CAPABLE;
mode_tmp = PT_MODE_OPERATIONAL;
prot_mode_tmp = PT_PROTOCOL_MODE_HID;
enum_status_tmp = ENUM_STATUS_FW_RESET_SENTINEL;
} else if (status == PIP2_RSP_ERR_NONE) {
if (boot == 0x00) {
pt_debug(dev, DL_ERROR,
"%s: PIP3 should not see this bit to be 0\n",
__func__);
goto exit;
} else {
dut_gen_tmp = DUT_PIP2_CAPABLE;
prot_mode_tmp = read_buf[PIP2_RESP_BODY_OFFSET + 2];
mode_tmp = read_buf[PIP2_RESP_BODY_OFFSET + 1];
if (mode_tmp == FW_SYS_MODE_SECONDARY_IMAGE)
mode_tmp = PT_MODE_SECONDARY_IMAGE;
else
mode_tmp = PT_MODE_OPERATIONAL;
enum_status_tmp = ENUM_STATUS_FW_RESET_SENTINEL;
}
}
exit:
mutex_lock(&cd->system_lock);
if (dut_gen)
*dut_gen = dut_gen_tmp;
if (mode)
*mode = mode_tmp;
if (prot_mode)
*prot_mode = prot_mode_tmp;
if (enum_status)
*enum_status = enum_status_tmp;
mutex_unlock(&cd->system_lock);
#ifdef TTDL_DIAGNOSTICS
pt_debug(dev, DL_INFO, "%s: Generation=%d Mode=%d Prot_mode=%d\n",
__func__, dut_gen_tmp, mode_tmp, prot_mode_tmp);
#endif /* TTDL_DIAGNOSTICS */
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_detect_dut_generation
*
* SUMMARY: Determine the generation and protocol mode of device.
* This function will Detect DUT:
* 1) By STATUS command with PIP2;
* 2) If 1) failed, it will use PIP3 STATUS command to dectect;
* 3) If 2) failed, it will use PIP1 REPORT_DESC command to detect.
*
* NOTE:
* If PIP2 BL is detected, this method can't identify the protocol_mode. It
* should be called again in ttdl_restart process.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *status - pointer to status bitmask
* *dut_gen - pointer to store the dut_generation
* *mode - pointer to store the pt_mode
* *prot_mode - pointer to store the protocol_mode
******************************************************************************/
static int _pt_detect_dut_generation(struct device *dev,
u32 *status, u8 *dut_gen, enum pt_mode *mode,
u8 *prot_mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int rc = 0;
/*
* If the protocol is set to HID, only attempt to detect the
* generation based on the HID protocol.
*/
if (cd->set_protocol_mode &&
cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
rc = _pt_detect_dut_by_pip3(dev, status, dut_gen, mode,
prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP3 detect\n",
__func__);
goto exit;
}
}
/*
* If the protocol is set to PIP, only attempt to detect the
* generation based on the PIP protocol.
*/
if (cd->set_protocol_mode &&
(cd->protocol_mode == PT_PROTOCOL_MODE_PIP ||
cd->protocol_mode == PT_PROTOCOL_MODE_HYBRID_HID)) {
rc = _pt_detect_dut_by_pip2(dev, status, dut_gen, mode,
prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP2 detect\n",
__func__);
goto exit;
}
rc = _pt_detect_dut_by_pip1(dev, status, dut_gen, mode,
prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP1 detect\n",
__func__);
goto exit;
}
pt_debug(dev, DL_WARN, "%s: Failed to detect DUT generation\n",
__func__);
goto exit;
}
/*
* If neither the protocol mode or generation were set by host,
* then try to detect the generation by any protocol
*/
rc = _pt_detect_dut_by_pip3(dev, status, dut_gen, mode, prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP3 detect\n",
__func__);
goto exit;
}
rc = _pt_detect_dut_by_pip2(dev, status, dut_gen, mode, prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP2 detect\n",
__func__);
goto exit;
}
rc = _pt_detect_dut_by_pip1(dev, status, dut_gen, mode, prot_mode);
if (!rc) {
pt_debug(dev, DL_INFO, "%s: Success with PIP1 detect\n",
__func__);
goto exit;
}
pt_debug(dev, DL_WARN, "%s: Failed to detect DUT generation\n",
__func__);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_dut_generation
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to get current dut generation.
*
* NOTE: This function WILL NOT try to determine dut generation.
*
* RETURN:
* The current dut generation.
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int _pt_request_dut_generation(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return cd->active_dut_generation;
}
/*******************************************************************************
* FUNCTION: _pt_detect_dut_mode()
*
* SUMMARY: Detect dut mode by PIP2/PIP3 STATUS command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *mode - pointer to store the pt_mode
******************************************************************************/
static int _pt_detect_dut_mode(struct device *dev, u8 *mode)
{
int rc = 0;
enum pt_mode tmp_mode;
struct pt_core_data *cd = dev_get_drvdata(dev);
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
cd->mode != PT_MODE_BOOTLOADER) {
rc = _pt_detect_dut_by_pip3(dev, NULL, NULL, &tmp_mode, NULL);
if (rc)
rc = _pt_detect_dut_by_pip2(dev, NULL, NULL, &tmp_mode,
NULL);
} else if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
rc = _pt_detect_dut_by_pip2(dev, NULL, NULL, &tmp_mode, NULL);
if (rc)
rc = _pt_detect_dut_by_pip3(dev, NULL, NULL, &tmp_mode,
NULL);
} else {
rc = _pt_detect_dut_by_pip2(dev, NULL, NULL, &tmp_mode, NULL);
}
if (!rc && mode)
*mode = tmp_mode;
return rc;
}
#define HW_VERSION_LEN_MAX 13
/*******************************************************************************
* FUNCTION: _legacy_generate_hw_version
*
* SUMMARY: Format chip infomation from struct ttdata (maintained by PIP1
* SYSINFO command) or struct bl_info (maintained by PIP1 BL INFOMATION
* command) to the hw_version.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hw_version - pointer to store the hardware version
******************************************************************************/
static int _legacy_generate_hw_version(struct pt_core_data *cd,
char *hw_version)
{
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
if (cd->sysinfo.ready) {
snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
ttdata->jtag_id_h, cd->pid_for_loader);
return 0;
} else if (cd->bl_info.ready) {
snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
cd->bl_info.chip_id, cd->pid_for_loader);
return 0;
} else {
snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
pt_debug(cd->dev, DL_ERROR,
"%s: SYSINFO and BL_INFO are not ready\n", __func__);
return -ENODATA;
}
}
/*******************************************************************************
* FUNCTION: _pip2_generate_hw_version
*
* SUMMARY: Format chip infomation from struct ttdata (maintained by PIP2
* VERSION command) to the hw_version.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *hw_version - pointer to store the hardware version
******************************************************************************/
static int _pip2_generate_hw_version(struct pt_core_data *cd, char *hw_version)
{
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
if (cd->app_pip_ver_ready | cd->bl_pip_ver_ready) {
snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.%04X.%02X",
ttdata->chip_id, ttdata->chip_rev, cd->pid_for_loader);
return 0;
} else {
snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
pt_debug(cd->dev, DL_ERROR,
"%s: PIP Version are not ready\n", __func__);
return -ENODATA;
}
}
/*******************************************************************************
* SUMMARY: Attempt to retrieve the HW version of the connected DUT
*
* NOTE: The calling function must ensure to free *hw_version
*
* RETURN:
* 0 = success
* !0 = Failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *hw_version - pointer to where the hw_version string will be stored
******************************************************************************/
static int _pt_request_hw_version(struct device *dev, char *hw_version)
{
int rc = 0;
u16 actual_read_len;
u16 pip_ver;
u8 rd_buf[256];
u8 status;
u8 index = PIP2_RESP_STATUS_OFFSET;
u8 return_data[8];
u8 panel_id;
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
if (!hw_version)
return -ENOMEM;
if (!cd->hw_detected) {
/* No HW detected */
rc = -ENODEV;
pt_debug(dev, DL_ERROR, "%s: no hardware is detected!\n",
__func__);
goto exit_error;
}
/* For Parade TC or TT parts */
if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
rc = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
NULL, 0, rd_buf, &actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to send PIP2 VERSION cmd\n",
__func__);
goto exit_error;
}
status = rd_buf[index];
if (status == 0) {
pip_ver = 256 * rd_buf[index + 2] + rd_buf[index + 1];
/*
* BL PIP 2.02 and greater the version fields are
* swapped
*/
if (pip_ver >= 0x0202) {
snprintf(hw_version, HW_VERSION_LEN_MAX,
"%02X%02X.%02X%02X.FF",
rd_buf[index + 10], rd_buf[index + 9],
rd_buf[index + 8], rd_buf[index + 7]);
} else {
snprintf(hw_version, HW_VERSION_LEN_MAX,
"%02X%02X.%02X%02X.FF",
rd_buf[index + 8], rd_buf[index + 7],
rd_buf[index + 10], rd_buf[index + 9]);
}
return STATUS_SUCCESS;
} else {
rc = status;
pt_debug(dev, DL_WARN,
"%s: PIP2 VERSION cmd response error\n",
__func__);
}
} else if (cd->active_dut_generation == DUT_PIP1_ONLY) {
/*
* For Parade/Cypress legacy parts the RevID and FamilyID are
* hard coded to FFFF
*/
if (cd->mode == PT_MODE_OPERATIONAL) {
rc = pt_hid_output_get_sysinfo(cd);
if (!rc) {
panel_id =
cd->sysinfo.sensing_conf_data.panel_id;
} else {
panel_id = PANEL_ID_NOT_ENABLED;
}
/* In FW - simply retrieve from ttdata struct */
snprintf(hw_version, HW_VERSION_LEN_MAX,
"%04X.FFFF.%02X",
ttdata->jtag_id_h,
panel_id);
return STATUS_SUCCESS;
} else {
/*
* Return the stored value if PT_PANEL_ID_BY_BL
* is not supported while other feature is.
*/
if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
rc = pt_hid_output_bl_get_information(
cd, return_data);
if (!rc) {
rc = pt_hid_output_bl_get_panel_id(
cd, &panel_id);
}
} else
panel_id = cd->pid_for_loader;
if (!rc) {
snprintf(hw_version,
HW_VERSION_LEN_MAX,
"%02X%02X.FFFF.%02X",
return_data[3], return_data[2],
panel_id);
return STATUS_SUCCESS;
}
}
} else {
/* Unknown generation */
rc = -ENODEV;
pt_debug(dev, DL_ERROR, "%s: generation is unkown!\n",
__func__);
}
exit_error:
snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
return rc;
}
/*******************************************************************************
* FUNCTION: pt_start_wd_timer
*
* SUMMARY: Starts the TTDL watchdog timer if the timer interval is > 0
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static void pt_start_wd_timer(struct pt_core_data *cd)
{
if (cd->watchdog_interval < 100) {
pt_debug(cd->dev, DL_ERROR,
"%s: WARNING: Invalid watchdog interval: %d\n",
__func__, cd->watchdog_interval);
return;
}
if (cd->watchdog_force_stop) {
pt_debug(cd->dev, DL_INFO,
"%s: TTDL WD Forced stop\n", __func__);
return;
}
mod_timer(&cd->watchdog_timer, jiffies +
msecs_to_jiffies(cd->watchdog_interval));
cd->watchdog_enabled = 1;
pt_debug(cd->dev, DL_DEBUG, "%s: TTDL WD Started\n", __func__);
}
/*******************************************************************************
* FUNCTION: pt_stop_wd_timer
*
* SUMMARY: Stops the TTDL watchdog timer if the timer interval is > 0
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static void pt_stop_wd_timer(struct pt_core_data *cd)
{
if (!cd->watchdog_interval)
return;
/*
* Ensure we wait until the watchdog timer
* running on a different CPU finishes
*/
del_timer_sync(&cd->watchdog_timer);
cancel_work_sync(&cd->watchdog_work);
del_timer_sync(&cd->watchdog_timer);
cd->watchdog_enabled = 0;
pt_debug(cd->dev, DL_DEBUG, "%s: TTDL WD Stopped\n", __func__);
}
/*******************************************************************************
* FUNCTION: pt_hw_soft_reset
*
* SUMMARY: Sends a PIP reset command to the DUT. Disable/re-enable the
* TTDL watchdog around the reset to ensure the WD doesn't happen to
* schedule an enum if it fires when the DUT is being reset.
* This can cause a double reset.
*
* NOTE: The WD MUST be stopped/restarted by the calling Function. Having
* the WD active could cause this function to fail!
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data struct
* protect - flag to call protected or non-protected
******************************************************************************/
static int pt_hw_soft_reset(struct pt_core_data *cd, int protect)
{
int rc = 0;
mutex_lock(&cd->system_lock);
cd->enum_status = ENUM_STATUS_START;
pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
mutex_unlock(&cd->system_lock);
if (protect)
rc = pt_hid_cmd_reset(cd);
else
rc = pt_hid_cmd_reset_(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: FAILED to execute SOFT reset\n", __func__);
return rc;
}
pt_debug(cd->dev, DL_INFO, "%s: SOFT reset successful\n",
__func__);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_hw_hard_reset
*
* SUMMARY: Calls the platform xres function if it exists to perform a hard
* reset on the DUT by toggling the XRES gpio. Disable/re-enable the
* TTDL watchdog around the reset to ensure the WD doesn't happen to
* schedule an enum if it fires when the DUT is being reset.
* This can cause a double reset.
*
* NOTE: The WD MUST be stopped/restarted by the calling Function. Having
* the WD active could cause this function to fail!
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data struct
******************************************************************************/
static int pt_hw_hard_reset(struct pt_core_data *cd)
{
if (cd->cpdata->xres) {
cd->enum_status = ENUM_STATUS_START;
pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n",
__func__);
cd->cpdata->xres(cd->cpdata, cd->dev);
pt_debug(cd->dev, DL_WARN, "%s: executed HARD reset\n",
__func__);
return 0;
}
pt_debug(cd->dev, DL_ERROR,
"%s: FAILED to execute HARD reset\n", __func__);
return -ENODEV;
}
/*******************************************************************************
* FUNCTION: pt_dut_reset
*
* SUMMARY: Attempts to reset the DUT by a hard reset and if that fails a
* soft reset.
*
* NOTE: The WD MUST be stopped/restarted by the calling Function. Having
* the WD active could cause this function to fail!
* NOTE: "protect" flag is only used for soft reset.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* protect - flag to call protected or non-protected
******************************************************************************/
static int pt_dut_reset(struct pt_core_data *cd, int protect)
{
int rc = 0;
pt_debug(cd->dev, DL_INFO, "%s: reset hw...\n", __func__);
mutex_lock(&cd->system_lock);
cd->hid_reset_cmd_state = 1;
rc = pt_hw_hard_reset(cd);
mutex_unlock(&cd->system_lock);
if (rc == -ENODEV) {
mutex_lock(&cd->system_lock);
cd->hid_reset_cmd_state = 0;
mutex_unlock(&cd->system_lock);
pt_debug(cd->dev, DL_ERROR,
"%s: Hard reset failed, try soft reset\n", __func__);
rc = pt_hw_soft_reset(cd, protect);
}
if (rc)
pt_debug(cd->dev, DL_ERROR, "%s: %s dev='%s' r=%d\n",
__func__, "Fail hw reset", dev_name(cd->dev), rc);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_dut_reset_and_wait
*
* SUMMARY: Wrapper function for pt_dut_reset that waits for the reset to
* complete
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int pt_dut_reset_and_wait(struct pt_core_data *cd)
{
int rc = 0;
int t;
rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
if (rc < 0)
goto exit;
if (cd->dual_mcu_available) {
pt_debug(cd->dev, DL_INFO,
"%s: Dual MCU is enabled, no BL sentinel after reset\n",
__func__);
usleep_range(10000, 11000);
cd->hid_reset_cmd_state = 0;
goto exit;
}
t = wait_event_timeout(cd->wait_q,
(cd->hid_reset_cmd_state == 0),
msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT));
if (IS_TMO(t)) {
#ifdef TTDL_DIAGNOSTICS
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
#endif /* TTDL_DIAGNOSTICS */
pt_debug(cd->dev, DL_ERROR, "%s: reset timed out\n",
__func__);
rc = -ETIME;
goto exit;
}
exit:
return rc;
}
/*
* touch default parameters (from report descriptor) to resolve protocol for
* touch report
*/
const struct pt_tch_abs_params tch_hdr_default[PT_TCH_NUM_HDR] = {
/* byte offset, size, min, max, bit offset, report */
{0x00, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* SCAN TIME */
{0x02, 0x01, 0x00, 0x20, 0x00, 0x01}, /* NUMBER OF RECORDS */
{0x02, 0x01, 0x00, 0x02, 0x05, 0x01}, /* LARGE OBJECT */
{0x03, 0x01, 0x00, 0x08, 0x00, 0x01}, /* NOISE EFFECT */
{0x03, 0x01, 0x00, 0x04, 0x06, 0x01}, /* REPORT_COUNTER */
};
/*
* button default parameters (from report descriptor) to resolve protocol for
* button report
*/
const struct pt_tch_abs_params tch_abs_default[PT_TCH_NUM_ABS] = {
/* byte offset, size, min, max, bit offset, report */
{0x02, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* X */
{0x04, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* Y */
{0x06, 0x01, 0x00, 0x100, 0x00, 0x01}, /* P (Z) */
{0x01, 0x01, 0x00, 0x20, 0x00, 0x01}, /* TOUCH ID */
{0x01, 0x01, 0x00, 0x04, 0x05, 0x01}, /* EVENT ID */
{0x00, 0x01, 0x00, 0x08, 0x00, 0x01}, /* OBJECT ID */
{0x01, 0x01, 0x00, 0x02, 0x07, 0x01}, /* LIFTOFF */
{0x07, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MAJOR */
{0x08, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MINOR */
{0x09, 0x01, 0x00, 0x100, 0x00, 0x01}, /* ORIENTATION */
};
/*******************************************************************************
* FUNCTION: pt_init_pip_report_fields
*
* SUMMARY: Setup default values for touch/button report parsing.
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_init_pip_report_fields(struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
memcpy(si->tch_hdr, tch_hdr_default, sizeof(tch_hdr_default));
memcpy(si->tch_abs, tch_abs_default, sizeof(tch_abs_default));
si->desc.tch_report_id = PT_PIP_TOUCH_REPORT_ID;
si->desc.tch_record_size = TOUCH_REPORT_SIZE;
si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID;
si->desc.pen_report_id = PT_HID_PEN_REPORT_ID;
si->desc.max_touch_num = MAX_TOUCH_NUM;
cd->features.easywake = 1;
cd->features.noise_metric = 1;
cd->features.tracking_heatmap = 1;
cd->features.sensor_data = 1;
}
/*******************************************************************************
* FUNCTION: pt_get_hid_report_
*
* SUMMARY: Get or create report. Must be called with cd->hid_report_lock
* acquired.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* *index - pointer to report index
* report_type - type of hid report
* report_id - id of hid report
* create - true: will create a new report
* false: will not create a new report
******************************************************************************/
static int pt_get_hid_report_(struct pt_core_data *cd, u8 *index,
u8 report_type, u8 report_id, bool create)
{
struct pt_hid_report *report = NULL;
int i;
int rc = 0;
/* Look for created reports */
for (i = 0; i < cd->num_hid_reports; i++) {
if (cd->hid_reports[i]->type == report_type &&
cd->hid_reports[i]->id == report_id) {
*index = i;
goto exit;
}
}
if (create && cd->num_hid_reports >= PT_HID_MAX_REPORTS) {
pt_debug(cd->dev, DL_WARN,
"%s: num_hid_reports=%d max=%d\n", __func__,
cd->num_hid_reports, PT_HID_MAX_REPORTS);
rc = -EINVAL;
} else if (create && cd->num_hid_reports < PT_HID_MAX_REPORTS) {
/* Create a new report */
report = kzalloc(sizeof(struct pt_hid_report),
GFP_KERNEL);
if (!report)
rc = -ENOMEM;
else {
report->type = report_type;
report->id = report_id;
*index = cd->num_hid_reports;
cd->hid_reports[cd->num_hid_reports++] = report;
}
}
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_free_hid_reports_
*
* SUMMARY: Free HID report. Must be called with cd->hid_report_lock acquired.
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_free_hid_reports_(struct pt_core_data *cd)
{
struct pt_hid_report *report;
int i, j;
for (i = 0; i < cd->num_hid_reports; i++) {
report = cd->hid_reports[i];
for (j = 0; j < report->num_fields; j++)
kfree(report->fields[j]);
kfree(report);
cd->hid_reports[i] = NULL;
}
cd->num_hid_reports = 0;
}
/*******************************************************************************
* FUNCTION: pt_free_hid_reports
*
* SUMMARY: Protected call to pt_free_hid_reports_() by a mutex lock.
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void pt_free_hid_reports(struct pt_core_data *cd)
{
mutex_lock(&cd->hid_report_lock);
pt_free_hid_reports_(cd);
mutex_unlock(&cd->hid_report_lock);
}
/*******************************************************************************
* FUNCTION: pt_create_hid_field_
*
* SUMMARY: Create field for HID report.Must be called with cd->hid_report_lock
* acquired.
*
* RETURN:
* pointer to hid field structure
*
* PARAMETERS:
* *report - pointer to hid report structure
******************************************************************************/
static struct pt_hid_field *pt_create_hid_field_(
struct pt_hid_report *report)
{
struct pt_hid_field *field;
if (!report)
return NULL;
if (report->num_fields == PT_HID_MAX_FIELDS)
return NULL;
field = kzalloc(sizeof(struct pt_hid_field), GFP_KERNEL);
if (!field)
return NULL;
field->report = report;
report->fields[report->num_fields++] = field;
return field;
}
/*******************************************************************************
* FUNCTION: get_hid_item_data
*
* SUMMARY: Get hid item data according to the item size.
*
* RETURN:
* 0 = no data
* !0 = data
*
* PARAMETERS:
* *data - pointer to item data
* item_size - the size of the item
******************************************************************************/
static inline int get_hid_item_data(u8 *data, int item_size)
{
if (item_size == 1)
return (int)*data;
else if (item_size == 2)
return (int)get_unaligned_le16(data);
else if (item_size == 4)
return (int)get_unaligned_le32(data);
return 0;
}
/*******************************************************************************
* FUNCTION: parse_report_descriptor
*
* SUMMARY: Parse report descriptor.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* *report_desc - pointer to report descriptor structure
* len - length of the data buffer to be parsed
******************************************************************************/
static int parse_report_descriptor(struct pt_core_data *cd,
u8 *report_desc, size_t len)
{
struct pt_hid_report *report;
struct pt_hid_field *field;
u8 *buf = report_desc;
u8 *end = buf + len;
int rc = 0;
int offset = 0;
int i, j;
u8 report_type;
u32 up_usage;
/* Global items */
u8 report_id = 0;
u16 usage_page = 0;
int report_count = 0;
int report_size = 0;
int logical_min = 0;
int logical_max = 0;
/* Local items */
u16 usage = 0;
/* Main items - Collection stack */
u32 collection_usages[PT_HID_MAX_NESTED_COLLECTIONS] = {0};
u8 collection_types[PT_HID_MAX_NESTED_COLLECTIONS] = {0};
u32 usages_pen[PT_HID_MAX_CONTINUOUS_USAGES] = {0};
/* First collection for header, second for report */
int logical_collection_count = 0;
int app_collection_count = 0;
int collection_nest = 0;
int usage_cnt = 0;
u8 report_index = 0;
pt_debug(cd->dev, DL_DEBUG, "%s: Report descriptor length: %u\n",
__func__, (u32)len);
mutex_lock(&cd->hid_report_lock);
pt_free_hid_reports_(cd);
while (buf < end) {
int item_type;
int item_size;
int item_tag;
u8 *data;
/* Get Item */
item_size = HID_GET_ITEM_SIZE(buf[0]);
if (item_size == 3)
item_size = 4;
item_type = HID_GET_ITEM_TYPE(buf[0]);
item_tag = HID_GET_ITEM_TAG(buf[0]);
data = ++buf;
buf += item_size;
/* Process current item */
switch (item_type) {
case HID_ITEM_TYPE_GLOBAL: /* 1 */
switch (item_tag) {
case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: /* 0 */
if (item_size == 0 || item_size == 4) {
rc = -EINVAL;
goto exit;
}
usage_page = (u16)get_hid_item_data(data,
item_size);
break;
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: /* 1 */
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
logical_min = get_hid_item_data(data,
item_size);
break;
case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: /* 2 */
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
logical_max = get_hid_item_data(data,
item_size);
break;
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: /* 3 */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Physical Min\n",
__func__);
break;
case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: /* 4 */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Physical Max\n",
__func__);
break;
case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: /* 5 */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Unit Exponent\n",
__func__);
break;
case HID_GLOBAL_ITEM_TAG_UNIT: /* 6 */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Unit\n",
__func__);
break;
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: /* 7 */
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
report_size = get_hid_item_data(data,
item_size);
break;
case HID_GLOBAL_ITEM_TAG_REPORT_ID: /* 8 */
if (item_size != 1) {
rc = -EINVAL;
goto exit;
}
report_id = get_hid_item_data(data, item_size);
offset = 0;
logical_collection_count = 0;
break;
case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: /* 9 */
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
report_count = get_hid_item_data(data,
item_size);
break;
case HID_GLOBAL_ITEM_TAG_PUSH: /* A */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Push\n",
__func__);
break;
case HID_GLOBAL_ITEM_TAG_POP: /* B */
pt_debug(cd->dev, DL_INFO,
"%s: TAG Ignored - Pop\n",
__func__);
break;
default:
pt_debug(cd->dev, DL_INFO,
"%s: Unrecognized Global tag 0x%X\n",
__func__, item_tag);
}
break;
case HID_ITEM_TYPE_LOCAL: /* 2 */
switch (item_tag) {
case HID_LOCAL_ITEM_TAG_USAGE:
if (item_size == 0 || item_size == 4) {
rc = -EINVAL;
goto exit;
}
usage = (u16)get_hid_item_data(data,
item_size);
if (report_id == PT_HID_PEN_REPORT_ID) {
usages_pen[usage_cnt++] =
usage_page << 16 | usage;
}
break;
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
/* usage_min = */
get_hid_item_data(data, item_size);
break;
case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
if (item_size == 0) {
rc = -EINVAL;
goto exit;
}
/* usage_max = */
get_hid_item_data(data, item_size);
break;
default:
pt_debug(cd->dev, DL_INFO,
"%s: Unrecognized Local tag %d\n",
__func__, item_tag);
}
break;
case HID_ITEM_TYPE_MAIN: /* 0 */
switch (item_tag) {
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
usage_cnt = 0;
if (item_size != 1) {
rc = -EINVAL;
goto exit;
}
if (PT_HID_MAX_NESTED_COLLECTIONS ==
collection_nest) {
rc = -EINVAL;
goto exit;
}
up_usage = usage_page << 16 | usage;
/* Update collection stack */
collection_usages[collection_nest] = up_usage;
collection_types[collection_nest] =
get_hid_item_data(data, item_size);
if (collection_types[collection_nest] ==
HID_COLLECTION_LOGICAL) {
logical_collection_count++;
app_collection_count = 0;
}
if (collection_types[collection_nest] ==
HID_COLLECTION_APPLICATION)
app_collection_count++;
collection_nest++;
break;
case HID_MAIN_ITEM_TAG_END_COLLECTION:
if (item_size != 0) {
rc = -EINVAL;
goto exit;
}
if (--collection_nest < 0) {
rc = -EINVAL;
goto exit;
}
break;
case HID_MAIN_ITEM_TAG_INPUT:
report_type = HID_INPUT_REPORT;
goto continue_main_item;
case HID_MAIN_ITEM_TAG_OUTPUT:
report_type = HID_OUTPUT_REPORT;
goto continue_main_item;
case HID_MAIN_ITEM_TAG_FEATURE:
report_type = HID_FEATURE_REPORT;
continue_main_item:
if (item_size != 1) {
pt_debug(cd->dev, DL_WARN,
"%s: %s=%d\n",
__func__,
"item_size", item_size);
rc = -EINVAL;
goto exit;
}
up_usage = usage_page << 16 | usage;
/* Get or create report */
rc = pt_get_hid_report_(cd, &report_index,
report_type, report_id, true);
if (rc) {
pt_debug(cd->dev, DL_WARN,
"%s: %s rc=%d\n",
__func__,
"get_hid_report failed", rc);
goto exit;
} else
report = cd->hid_reports[report_index];
if (!report->usage_page && collection_nest > 0)
report->usage_page =
collection_usages
[collection_nest - 1];
if (report->id == PT_HID_PEN_REPORT_ID
&& report_count > 1)
goto continue_pen_usages;
/* Create field */
field = pt_create_hid_field_(report);
if (!field) {
pt_debug(cd->dev, DL_WARN,
"%s: %s\n",
__func__,
"create field failed");
rc = -ENOMEM;
goto exit;
}
field->report_count = report_count;
field->report_size = report_size;
field->size = report_count
* report_size;
field->offset = offset;
field->data_type =
get_hid_item_data(data,
item_size);
field->logical_min = logical_min;
field->logical_max = logical_max;
field->usage_page = up_usage;
usages_pen[0] = 0;
for (j = 0; j < collection_nest; j++) {
field->collection_usage_pages
[collection_types[j]] =
collection_usages[j];
}
goto exit_main_item;
continue_pen_usages:
for (i = 0; i < report_count; i++) {
/* Create field */
field = pt_create_hid_field_(
report);
if (!field) {
pt_debug(cd->dev, DL_WARN,
"%s: Pen - %s\n",
__func__,
"create field failed");
rc = -ENOMEM;
goto exit;
}
field->report_size = report_size;
field->size = report_count *
report_size;
field->report_count = 1;
field->offset = offset + i*report_size;
field->data_type =
get_hid_item_data(data,
item_size);
field->logical_min = logical_min;
field->logical_max = logical_max;
field->usage_page = usages_pen[i];
usages_pen[i] = 0;
for (j = 0; j < collection_nest; j++) {
field->collection_usage_pages
[collection_types[j]] =
collection_usages[j];
}
}
exit_main_item:
/* Update report's header or record size */
if (app_collection_count == 1) {
report->header_size += field->size;
} else if (logical_collection_count == 1) {
field->record_field = true;
field->offset -= report->header_size;
/* Set record field index */
if (report->record_field_index == 0)
report->record_field_index =
report->num_fields - 1;
report->record_size += field->size;
}
report->size += field->size;
report->log_collection_num =
logical_collection_count;
offset += field->size;
usage_cnt = 0;
break;
default:
pt_debug(cd->dev, DL_INFO,
"%s: Unrecognized Main tag %d\n",
__func__, item_tag);
}
/* Reset all local items */
usage = 0;
break;
}
}
if (buf != end) {
pt_debug(cd->dev, DL_ERROR,
"%s: Report descriptor length invalid\n",
__func__);
rc = -EINVAL;
goto exit;
}
if (collection_nest) {
pt_debug(cd->dev, DL_ERROR,
"%s: Unbalanced collection items (%d)\n",
__func__, collection_nest);
rc = -EINVAL;
goto exit;
}
exit:
if (rc)
pt_free_hid_reports_(cd);
mutex_unlock(&cd->hid_report_lock);
return rc;
}
/*******************************************************************************
* FUNCTION: find_report_desc_field
*
* SUMMARY: Find the corresponding field from report according to the usage page
* and collection usage page.
*
* RETURN:
* pointer to hid field structure
*
* PARAMETERS:
* *cd - pointer to core data structure
* usage_page - hid usage page
* collection_usage_page - hid collection usage page
* collection_type - hid collection type
******************************************************************************/
static struct pt_hid_field *find_report_desc_field(
struct pt_core_data *cd, u32 usage_page,
u32 collection_usage_page, u8 collection_type)
{
struct pt_hid_report *report = NULL;
struct pt_hid_field *field = NULL;
int i;
int j;
u32 field_cup;
u32 field_up;
for (i = 0; i < cd->num_hid_reports; i++) {
report = cd->hid_reports[i];
for (j = 0; j < report->num_fields; j++) {
field = report->fields[j];
field_cup = field->collection_usage_pages
[collection_type];
field_up = field->usage_page;
if (field_cup == collection_usage_page
&& field_up == usage_page) {
return field;
}
}
}
return NULL;
}
/*******************************************************************************
* FUNCTION: fill_tch_abs
*
* SUMMARY: Fill touch abs with hid field.
*
* PARAMETERS:
* *cd - pointer to core data structure
* *field - pointer to hid field structure
******************************************************************************/
static void fill_tch_abs(struct pt_tch_abs_params *tch_abs,
struct pt_hid_field *field)
{
tch_abs->ofs = field->offset / 8;
tch_abs->size = field->report_size / 8;
if (field->report_size % 8)
tch_abs->size += 1;
tch_abs->min = 0;
tch_abs->max = 1 << field->report_size;
tch_abs->bofs = field->offset - (tch_abs->ofs << 3);
tch_abs->logical_max = field->logical_max;
tch_abs->logical_min = field->logical_min;
}
/*******************************************************************************
* FUNCTION: find_report_desc
*
* SUMMARY: Find out the corresponding report based on the usage page.
*
* RETURN:
* pointer to hid report structure
*
* PARAMETERS:
* *cd - pointer to core data structure
* id - report id
* usage_page - hid usage page
******************************************************************************/
static struct pt_hid_report *find_report_desc(struct pt_core_data *cd,
u8 id, u32 usage_page)
{
struct pt_hid_report *report = NULL;
int i;
for (i = 0; i < cd->num_hid_reports; i++) {
if (cd->hid_reports[i]->usage_page == usage_page &&
cd->hid_reports[i]->id == id) {
report = cd->hid_reports[i];
break;
}
}
return report;
}
/*******************************************************************************
* FUNCTION: setup_pen_report_from_report_desc
*
* SUMMARY: Setup values for pen report according to report descriptor.
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void setup_pen_report_from_report_desc(
struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
struct pt_hid_report *report;
struct pt_hid_field *field;
int i;
u32 pen_collection_usage_page = HID_PT_PEN_COL_USAGE_PG;
u8 id = PT_HID_PEN_REPORT_ID;
/*
* Search each pen abs field. If found, fill the values into the
* pen struct.If not, mark this pen field as invalid (report = 0).
*/
for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) {
field = find_report_desc_field(cd,
pt_pen_abs_field_map[i],
pen_collection_usage_page,
HID_COLLECTION_PHYSICAL);
if (field) {
pt_debug(cd->dev, DL_DEBUG,
" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
field, field->report_count, field->report_size,
field->offset, field->data_type,
field->logical_min, field->logical_max,
field->usage_page);
fill_tch_abs(&si->pen_abs[i], field);
si->pen_abs[i].report = 2;
pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d\n",
pt_pen_abs_string[i],
(u32)si->pen_abs[i].ofs,
(u32)si->pen_abs[i].size,
(u32)si->pen_abs[i].min,
(u32)si->pen_abs[i].max,
(u32)si->pen_abs[i].bofs,
si->pen_abs[i].report);
} else {
si->pen_abs[i].report = 0;
pt_debug(cd->dev, DL_DEBUG, "%s: report:%d\n",
pt_pen_abs_string[i], si->pen_abs[i].report);
}
}
report = find_report_desc(cd, id, pen_collection_usage_page);
if (report)
si->desc.pen_report_id = report->id;
else
si->desc.pen_report_id = PT_HID_PEN_REPORT_ID;
}
/*******************************************************************************
* FUNCTION: setup_finger_report_from_report_desc
*
* SUMMARY: Setup values for finger report according to report descriptor.
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static void setup_finger_report_from_report_desc(
struct pt_core_data *cd)
{
struct pt_sysinfo *si = &cd->sysinfo;
struct pt_hid_report *report;
struct pt_hid_field *field;
u32 tch_collection_usage_page = HID_PT_TCH_COL_USAGE_PG;
u8 id = PT_HID_FINGER_REPORT_ID;
int i;
/*
* Search each touch abs field. If found, fill the values into the
* abs struct. If not, mark this abs field as invalid (report = 0).
*/
for (i = PT_TCH_X; i < PT_TCH_NUM_ABS; i++) {
field = find_report_desc_field(cd,
pt_tch_abs_field_map[i],
tch_collection_usage_page,
HID_COLLECTION_APPLICATION);
if (field) {
pt_debug(cd->dev, DL_DEBUG,
" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
field, field->report_count, field->report_size,
field->offset, field->data_type,
field->logical_min, field->logical_max,
field->usage_page);
fill_tch_abs(&si->tch_abs[i], field);
si->tch_abs[i].report = 1;
pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d\n",
pt_tch_abs_string[i],
(u32)si->tch_abs[i].ofs,
(u32)si->tch_abs[i].size,
(u32)si->tch_abs[i].min,
(u32)si->tch_abs[i].max,
(u32)si->tch_abs[i].bofs,
si->tch_abs[i].report);
} else {
si->tch_abs[i].report = 0;
}
}
/*
* Search each touch header field. If found, fill the values into
* the header struct. If not, mark this header field as invalid
* (report = 0).
*/
for (i = PT_TCH_TIME; i < PT_TCH_NUM_HDR; i++) {
field = find_report_desc_field(cd,
pt_tch_hdr_field_map[i],
tch_collection_usage_page,
HID_COLLECTION_APPLICATION);
if (field) {
pt_debug(cd->dev, DL_DEBUG,
" Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
field, field->report_count, field->report_size,
field->offset, field->data_type,
field->logical_min, field->logical_max,
field->usage_page);
fill_tch_abs(&si->tch_hdr[i], field);
si->tch_hdr[i].report = 1;
pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d\n",
pt_tch_hdr_string[i],
(u32)si->tch_hdr[i].ofs,
(u32)si->tch_hdr[i].size,
(u32)si->tch_hdr[i].min,
(u32)si->tch_hdr[i].max,
(u32)si->tch_hdr[i].bofs,
si->tch_hdr[i].report);
} else {
si->tch_hdr[i].report = 0;
}
}
si->desc.max_touch_num = si->tch_hdr[PT_TCH_NUM].max;
report = find_report_desc(cd, id, tch_collection_usage_page);
if (report) {
si->desc.tch_report_id = report->id;
/* First, report->record_size and report->header_size are based
* on 'BIT', they need to be divided by 8 to get tch_record_size
* and tch_header_size, which are based on 'BYTE'.
* Second, tch_header_size needs to add 3 bytes: 2 bytes length,
* 1 byte report id.
*/
si->desc.tch_record_size = report->record_size / 8;
si->desc.tch_header_size = (report->header_size / 8) + 3;
si->desc.max_tch_per_packet = report->log_collection_num;
} else {
si->desc.tch_report_id = PT_HID_FINGER_REPORT_ID;
si->desc.tch_record_size = TOUCH_REPORT_SIZE;
si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
si->desc.max_tch_per_packet = MAX_TOUCH_NUM;
}
pt_debug(cd->dev, DL_DEBUG,
"%s: tch_record_size:%d, tch_header_size:%d, max_touch_num:%d\n",
__func__,
si->desc.tch_record_size,
si->desc.tch_header_size,
si->desc.max_touch_num);
}
/*******************************************************************************
* FUNCTION: publish_report_desc
*
* SUMMARY: If TTHE_TUNER_SUPPORT is defined print the report descriptor data
* into the tthe_tuner sysfs node under the label "HIDRptDesc".
*
* PARAMETERS:
* *cd - pointer to core data
* len - Report descriptor length
******************************************************************************/
static void publish_report_desc(struct pt_core_data *cd, u16 len)
{
int max_bytes = 300;
int pr_bytes = 0;
int size = len;
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, cd->response_buf, size,
"HIDRptDesc=");
#endif
while (size > max_bytes) {
pt_pr_buf(cd->dev, DL_DEBUG,
cd->response_buf + pr_bytes,
max_bytes, "<<< HIDRptDesc");
pr_bytes += max_bytes;
size -= max_bytes;
}
if (size > 0)
pt_pr_buf(cd->dev, DL_DEBUG,
cd->response_buf + pr_bytes,
size, "<<< HIDRptDesc");
}
/*******************************************************************************
* FUNCTION: pt_get_report_descriptor_
*
* SUMMARY: Get and parse report descriptor.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_get_report_descriptor_(struct pt_core_data *cd)
{
struct device *dev = cd->dev;
struct pt_hid_cmd hid_cmd = {
.descriptor = cd->hid_desc.report_desc_register,
.read_length = cd->hid_core.hid_report_desc_len,
};
int rc;
int t;
u8 *desc;
u16 desc_len;
rc = pt_hid_send_command_(cd, &hid_cmd);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: failed to get HID report descriptor length, rc=%d\n",
__func__, rc);
goto exit;
}
desc = cd->response_buf;
desc_len = cd->hid_core.hid_report_desc_len;
/* Remove lenth field and report id to prepare parse work */
if (cd->protocol_mode == PT_PROTOCOL_MODE_PIP) {
desc = cd->response_buf + 3;
desc_len -= 3;
}
publish_report_desc(cd, cd->hid_core.hid_report_desc_len);
rc = parse_report_descriptor(cd, desc, desc_len);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error parsing report descriptor rc=%d\n",
__func__, rc);
}
pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n",
__func__, cd->num_hid_reports);
for (t = 0; t < cd->num_hid_reports; t++) {
struct pt_hid_report *report = cd->hid_reports[t];
int j;
pt_debug(cd->dev, DL_DEBUG,
"Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n",
t, report->type, report->id,
report->size, report->num_fields,
report->record_field_index, report->header_size,
report->record_size, report->usage_page);
if (report->id == PT_HID_FINGER_REPORT_ID)
pt_debug(cd->dev, DL_INFO, "%s: logical collection number: %d\n",
__func__, report->log_collection_num);
for (j = 0; j < report->num_fields; j++) {
struct pt_hid_field *field = report->fields[j];
pt_debug(cd->dev, DL_DEBUG,
" Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n",
j, field->report_count, field->report_size,
field->offset, field->data_type,
field->logical_min, field->logical_max,
field->usage_page);
pt_debug(cd->dev, DL_DEBUG, " Collections Phys:%08X App:%08X Log:%08X\n",
field->collection_usage_pages
[HID_COLLECTION_PHYSICAL],
field->collection_usage_pages
[HID_COLLECTION_APPLICATION],
field->collection_usage_pages
[HID_COLLECTION_LOGICAL]);
}
}
setup_pen_report_from_report_desc(cd);
setup_finger_report_from_report_desc(cd);
/* Free it for now */
pt_free_hid_reports_(cd);
pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n",
__func__, cd->num_hid_reports);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_get_report_descriptor
*
* SUMMARY: Protected call to pt_get_report_descriptor_()
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_get_report_descriptor(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_get_report_descriptor_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_get_mode
*
* SUMMARY: Determine the current mode from the contents of a HID descriptor
* message
*
* RETURN: Enum of the current mode
*
* PARAMETERS:
* *cd - pointer to the Core Data structure
* protect - run command in protected mode
* *mode - pointer to store the retrieved mode
******************************************************************************/
static int pt_get_mode(struct pt_core_data *cd, struct pt_hid_desc *desc)
{
if (desc->packet_id == HID_APP_REPORT_ID)
return PT_MODE_OPERATIONAL;
else if (desc->packet_id == HID_BL_REPORT_ID)
return PT_MODE_BOOTLOADER;
return PT_MODE_UNKNOWN;
}
/*******************************************************************************
* FUNCTION: _pt_request_get_mode
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to determine the current mode of the DUT by use of the Get HID
* Descriptor command.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - run command in protected mode
* *mode - pointer to store the retrieved mode
******************************************************************************/
static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct pt_hid_desc hid_desc;
int rc = 0;
memset(&hid_desc, 0, sizeof(hid_desc));
if (protect)
rc = pt_get_hid_descriptor(cd, &hid_desc);
else
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc)
*mode = PT_MODE_UNKNOWN;
else
*mode = pt_get_mode(cd, &hid_desc);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_queue_enum_
*
* SUMMARY: Queues a TTDL enum by scheduling work with the pt_enum_with_dut()
* function. It won't try to add/delete sysfs node or modules.
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static void pt_queue_enum_(struct pt_core_data *cd)
{
if (cd->startup_state == STARTUP_NONE ||
cd->startup_state == STARTUP_DONE) {
cd->startup_state = STARTUP_QUEUED;
#ifdef TTDL_DIAGNOSTICS
if (!cd->bridge_mode)
schedule_work(&cd->enum_work);
else
cd->startup_state = STARTUP_NONE;
#else
schedule_work(&cd->enum_work);
#endif
pt_debug(cd->dev, DL_INFO,
"%s: enum_work queued\n", __func__);
} else {
pt_debug(cd->dev, DL_WARN,
"%s: Enum not queued - startup_state = %d\n",
__func__, cd->startup_state);
}
}
/*******************************************************************************
* FUNCTION: pt_queue_enum
*
* SUMMARY: Queues a TTDL enum within a mutex lock
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static void pt_queue_enum(struct pt_core_data *cd)
{
mutex_lock(&cd->system_lock);
pt_queue_enum_(cd);
mutex_unlock(&cd->system_lock);
}
static void remove_sysfs_and_modules(struct device *dev);
/*******************************************************************************
* FUNCTION: pt_queue_restart
*
* SUMMARY: Queues a TTDL restart within a mutex lock
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
* remove_sysfs_module - True: remove all DUT relative sysfs nodes and modules
* False: will not perform remove action
******************************************************************************/
static void pt_queue_restart(struct pt_core_data *cd)
{
mutex_lock(&cd->system_lock);
if (cd->startup_state == STARTUP_NONE ||
cd->startup_state == STARTUP_DONE) {
cd->startup_state = STARTUP_QUEUED;
schedule_work(&cd->ttdl_restart_work);
pt_debug(cd->dev, DL_INFO,
"%s: pt_ttdl_restart queued\n", __func__);
} else {
pt_debug(cd->dev, DL_INFO, "%s: startup_state = %d\n",
__func__, cd->startup_state);
}
mutex_unlock(&cd->system_lock);
}
/*******************************************************************************
* FUNCTION: call_atten_cb
*
* SUMMARY: Iterate over attention list call the function that registered.
*
* RETURN: void
*
* PARAMETERS:
* *cd - pointer to core data
* type - type of attention list
* mode - condition for execution
******************************************************************************/
static void call_atten_cb(struct pt_core_data *cd,
enum pt_atten_type type, int mode)
{
struct atten_node *atten, *atten_n;
pt_debug(cd->dev, DL_DEBUG, "%s: check list type=%d mode=%d\n",
__func__, type, mode);
spin_lock(&cd->spinlock);
list_for_each_entry_safe(atten, atten_n,
&cd->atten_list[type], node) {
if (!mode || atten->mode & mode) {
spin_unlock(&cd->spinlock);
pt_debug(cd->dev, DL_DEBUG,
"%s: attention for '%s'",
__func__, dev_name(atten->dev));
atten->func(atten->dev);
spin_lock(&cd->spinlock);
}
}
spin_unlock(&cd->spinlock);
}
/*******************************************************************************
* FUNCTION: start_fw_upgrade
*
* SUMMARY: Calling "PT_ATTEN_LOADER" attention list that loader registered to
* start firmware upgrade.
*
* RETURN:
* 0 = success
*
* PARAMETERS:
* *data - pointer to core data
******************************************************************************/
static int start_fw_upgrade(void *data)
{
struct pt_core_data *cd = (struct pt_core_data *)data;
call_atten_cb(cd, PT_ATTEN_LOADER, 0);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_put_device_into_easy_wakeup_
*
* SUMMARY: Call the enter_easywake_state function and set the device into easy
* wake up state.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd)
{
int rc = 0;
u8 status = 0;
mutex_lock(&cd->system_lock);
cd->wait_until_wake = 0;
mutex_unlock(&cd->system_lock);
rc = pt_hid_output_enter_easywake_state_(cd,
cd->easy_wakeup_gesture, &status);
if (rc || status == 0)
return -EBUSY;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_put_device_into_deep_sleep_
*
* SUMMARY: Call the set_power function and set the DUT to deep sleep
*
* RETURN:
* 0 = success
* !0 = error
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_put_device_into_deep_sleep_(struct pt_core_data *cd)
{
int rc = 0;
rc = pt_hid_cmd_set_power_(cd, HID_POWER_SLEEP);
if (rc)
rc = -EBUSY;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_put_device_into_deep_standby_
*
* SUMMARY: Call the set_power function and set the DUT to Deep Standby
*
* RETURN:
* 0 = success
* !0 = error
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_put_device_into_deep_standby_(struct pt_core_data *cd)
{
int rc = 0;
rc = pt_hid_cmd_set_power_(cd, HID_POWER_STANDBY);
if (rc)
rc = -EBUSY;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_poweroff_device_
*
* SUMMARY: Disable IRQ and HW power down the device.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_poweroff_device_(struct pt_core_data *cd)
{
int rc;
if (cd->irq_enabled) {
cd->irq_enabled = false;
disable_irq_nosync(cd->irq);
}
rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0);
if (rc < 0)
pt_debug(cd->dev, DL_ERROR, "%s: HW Power down fails r=%d\n",
__func__, rc);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_sleep_
*
* SUMMARY: Suspend the device with power off or deep sleep based on the
* configuration in the core platform data structure.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_sleep_(struct pt_core_data *cd)
{
int rc = 0;
/*
* Do nothing if system already sleeping or in progress of
* entering sleep. Proceed if awake or waking.
*/
if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING)
goto exit;
mutex_lock(&cd->system_lock);
cd->sleep_state = SS_SLEEPING;
mutex_unlock(&cd->system_lock);
/* Ensure watchdog and startup works stopped */
pt_stop_wd_timer(cd);
cancel_work_sync(&cd->enum_work);
pt_stop_wd_timer(cd);
// Ensure device is awake to send i2c command
pm_stay_awake(cd->dev);
if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
rc = pt_put_device_into_easy_wakeup_(cd);
else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP)
rc = pt_core_poweroff_device_(cd);
else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY)
rc = pt_put_device_into_deep_standby_(cd);
else
rc = pt_put_device_into_deep_sleep_(cd);
pm_relax(cd->dev);
mutex_lock(&cd->system_lock);
if (rc == 0) {
cd->sleep_state = SS_SLEEP_ON;
} else {
cd->sleep_state = SS_SLEEP_OFF;
pt_start_wd_timer(cd);
}
mutex_unlock(&cd->system_lock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_sleep
*
* SUMMARY: Protected call to pt_core_sleep_ by exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_sleep(struct pt_core_data *cd)
{
int rc = 0;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_core_sleep_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: fail to release exclusive\n", __func__);
else
pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
__func__);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_wakeup_host
*
* SUMMARY: Check wake up report and call the PT_ATTEN_WAKE attention list.
*
* NOTE: TSG5 EasyWake and TSG6 EasyWake use different protocol.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_wakeup_host(struct pt_core_data *cd)
{
#ifndef EASYWAKE_TSG6
/* TSG5 EasyWake */
int rc = 0;
int event_id;
int size = get_unaligned_le16(&cd->input_buf[0]);
/* Validate report */
if (size != 4 || cd->input_buf[2] != 4)
rc = -EINVAL;
event_id = cd->input_buf[3];
pt_debug(cd->dev, DL_INFO, "%s: e=%d, rc=%d\n",
__func__, event_id, rc);
if (rc) {
pt_core_sleep_(cd);
goto exit;
}
/* attention WAKE */
call_atten_cb(cd, PT_ATTEN_WAKE, 0);
exit:
return rc;
#else
/* TSG6 FW1.3 EasyWake */
int rc = 0;
int i = 0;
int report_length;
/* Validate report */
if (cd->input_buf[2] != PT_PIP_WAKEUP_REPORT_ID) {
rc = -EINVAL;
pt_core_sleep_(cd);
goto exit;
}
/* Get gesture id and gesture data length */
cd->gesture_id = cd->input_buf[3];
report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]);
cd->gesture_data_length = report_length - 4;
pt_debug(cd->dev, DL_INFO,
"%s: gesture_id = %d, gesture_data_length = %d\n",
__func__, cd->gesture_id, cd->gesture_data_length);
for (i = 0; i < cd->gesture_data_length; i++)
cd->gesture_data[i] = cd->input_buf[4 + i];
/* attention WAKE */
call_atten_cb(cd, PT_ATTEN_WAKE, 0);
exit:
return rc;
#endif
}
/*******************************************************************************
* FUNCTION: pt_get_touch_field
*
* SUMMARY: Function to calculate touch fields. The field refers to each element
* in the input report, such as "Number of Records", "X-axis". This function
* can calculate the value of element based on the bit offset, size and the
* max value of the element.
*
* PARAMETERS:
* *dev - pointer to device structure
* *field - pointer to field calculation result
* size - size in bytes
* max - max value of result
* *data - pointer to input data to be parsed
* bofs - bit offset
******************************************************************************/
void pt_get_touch_field(struct device *dev,
int *field, int size, int max, u8 *data, int bofs)
{
int nbyte;
int next;
for (nbyte = 0, *field = 0, next = 0; nbyte < size; nbyte++) {
pt_debug(dev, DL_DEBUG,
"%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d) bofs=%d\n",
__func__, *field, *field, size, max, data, next,
data[next], data[next], bofs);
*field = *field + ((data[next] >> bofs) << (nbyte * 8));
next++;
}
if (max > 0)
*field &= max - 1;
pt_debug(dev, DL_DEBUG,
"%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d)\n",
__func__, *field, *field, size, max, data, next,
data[next], data[next]);
}
/*******************************************************************************
* FUNCTION: move_tracking_heatmap_data
*
* SUMMARY: Move the valid tracking heatmap data from the input buffer into the
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
* the tthe_tuner sysfs node under the label "THM"
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_tracking_heatmap_data(struct pt_core_data *cd,
struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
int size = get_unaligned_le16(&cd->input_buf[0]);
if (size)
tthe_print(cd, cd->input_buf, size, "THM=");
#endif
memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
return 0;
}
/*******************************************************************************
* FUNCTION: move_sensor_data
*
* SUMMARY: Move the valid sensor data from the input buffer into the system
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
* the tthe_tuner sysfs node under the label "sensor_monitor"
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_sensor_data(struct pt_core_data *cd,
struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
int size = get_unaligned_le16(&cd->input_buf[0]);
if (size)
tthe_print(cd, cd->input_buf, size, "sensor_monitor=");
#endif
memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
return 0;
}
/*******************************************************************************
* FUNCTION: hid_async_report_validate_crc
*
* SUMMARY: Validate CRC for pure hid asynchronous reports.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int hid_async_report_validate_crc(struct pt_core_data *cd)
{
int rc = 0;
int hid_pl_len;
int hdr_sz = HID_RESP_HEADER_SIZE;
unsigned short calc_crc = 0;
unsigned short resp_crc = 0;
hid_pl_len = get_unaligned_le16(
&cd->pt_async_rpt.buf[HID_RESP_PAYLOAD_LEN_OFFSET]);
calc_crc = crc_ccitt_calculate(
&cd->pt_async_rpt.buf[HID_RESP_PAYLOAD_LEN_OFFSET],
hid_pl_len - 2);
resp_crc = cd->pt_async_rpt.buf[hid_pl_len + hdr_sz - 2] << 8;
resp_crc |= cd->pt_async_rpt.buf[hid_pl_len + hdr_sz - 1];
if (resp_crc != calc_crc) {
pt_debug(cd->dev, DL_ERROR,
"%s: RTSD CRC ERR: calc=0x%04X rsp=0x%04X\n",
__func__, calc_crc, resp_crc);
rc = -EINVAL;
}
return rc;
}
/*******************************************************************************
* FUNCTION: stitch_hid_async_report
*
* SUMMARY: Stitch the asynchronous reports in pure hid mode. HID reports can be
* segmented due to wMaxInputReportLength being less than the size of a full
* PIP3 response. When needed TTDL can stitch these segmented reports back into
* a contiguous PIP3 report. The segmented reports MUST begin with the First
* Report (FRpt) flag being set to 1 and TTDL will continue stitching until the
* More Report (MRpt) flag is 0. The PIP3 Length filed must reflect the length
* of the stitched report and the PIP3 CRC must match or the report is ignored.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *data_buf - pointer to the async reports buffer
* mrpt - the value of more reports bit
******************************************************************************/
static int stitch_hid_async_report(struct pt_core_data *cd)
{
static int input_sz;
int hdr_sz = HID_RESP_HEADER_SIZE;
int pkt_sz = cd->hid_core.hid_max_input_len;
int rc = 0;
u8 frpt = cd->pt_async_rpt.frpt;
static int hid_pl_len;
int data_len = 0;
int size = get_unaligned_le16(&cd->input_buf[0]);
if (frpt) {
if (input_sz)
pt_debug(cd->dev, DL_WARN,
"%s: Unexpected start of new async report detected, previous report discarded\n",
__func__);
/* Field Check: packet size */
if (size != pkt_sz) {
pt_debug(cd->dev, DL_ERROR,
"%s: bad message, first pcket size (%d) error\n",
__func__, size);
rc = -EBADMSG;
goto exit;
}
memset(cd->pt_async_rpt.buf, 0, sizeof(cd->pt_async_rpt.buf));
memcpy(cd->pt_async_rpt.buf, cd->input_buf, pkt_sz);
hid_pl_len = get_unaligned_le16(
&cd->pt_async_rpt.buf[HID_RESP_PAYLOAD_LEN_OFFSET]);
input_sz = pkt_sz;
} else {
if (input_sz == 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Start of async report was not detected. Current report discarded\n",
__func__);
rc = -EBADMSG;
goto exit;
}
data_len = input_sz + size - hdr_sz;
if (data_len > PT_MAX_PIP2_MSG_SIZE) {
pt_debug(cd->dev, DL_ERROR, "%s: error data length (%d)\n",
__func__, data_len);
rc = -EBADMSG;
goto exit;
}
memcpy(&cd->pt_async_rpt.buf[input_sz], &cd->input_buf[hdr_sz],
size - hdr_sz);
input_sz = data_len;
}
if (cd->pt_async_rpt.mrpt &&
((input_sz - hdr_sz) >= hid_pl_len)) {
pt_debug(cd->dev, DL_ERROR,
"%s: Extra report detected, stitched size greater than payload length\n",
__func__);
rc = -EBADMSG;
goto exit;
}
if (cd->pt_async_rpt.mrpt == 0) {
cd->pt_async_rpt.buf[0] = LOW_BYTE(input_sz);
cd->pt_async_rpt.buf[1] = HI_BYTE(input_sz);
pt_pr_buf(cd->dev, DL_DEBUG, cd->pt_async_rpt.buf,
input_sz, "async_sensor_data");
input_sz = 0;
hid_pl_len = 0;
rc = hid_async_report_validate_crc(cd);
if (rc)
pt_debug(cd->dev, DL_ERROR, "%s: CRC error\n",
__func__);
}
exit:
if (rc) {
input_sz = 0;
hid_pl_len = 0;
memset(cd->pt_async_rpt.buf, 0, sizeof(cd->pt_async_rpt.buf));
}
return rc;
}
/*******************************************************************************
* FUNCTION: rebuild_hid_sdm_to_pip1
*
* SUMMARY: Rebuild async_data_buf with PIP1 data format for SENSOR DATA MODE
* response.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int rebuild_hid_sdm_to_pip1(struct pt_core_data *cd)
{
int hid_pl_len = 0;
u8 cmd_id = 0;
int dp_count = 0;
u8 data_buf[PT_MAX_PIP1_MSG_SIZE];
int pip1_pl_len;
int i = 0;
int rc = 0;
hid_pl_len = get_unaligned_le16(
&cd->pt_async_rpt.buf[HID_RESP_PAYLOAD_LEN_OFFSET]);
cmd_id = cd->pt_async_rpt.buf[HID_RESP_CMD_ID_OFFSET] & 0x7F;
if (cmd_id != HID_CMD_ID_START_SENSOR_DATA_MODE) {
pt_debug(cd->dev, DL_ERROR,
"%s: Cmd ID error. cmd_id=0x%02X\n",
__func__, cmd_id);
rc = -EINVAL;
goto exit;
}
memset(data_buf, 0, sizeof(data_buf));
/*
* Data point count is calculated by:
* ( Length of Payload -
* Payload header size (4) -
* Timestamp (2) -
* CRC(2)) / 6
*/
dp_count = (hid_pl_len - 8) / 6;
pt_debug(cd->dev, DL_INFO, "%s: dp_count=%d\n",
__func__, dp_count);
if (dp_count > 25) {
pt_debug(cd->dev, DL_ERROR,
"%s: Overflow! Data Points Count=%d\n",
__func__, dp_count);
rc = -EBADMSG;
goto exit;
}
pip1_pl_len = SENSOR_HEADER_SIZE +
PIP1_SENSOR_DATA_MODE_DP_COUNT * 6;
/* Set Length Field */
cd->pt_async_rpt.buf[0] = LOW_BYTE(pip1_pl_len);
cd->pt_async_rpt.buf[1] = HI_BYTE(pip1_pl_len);
/* Report ID */
cd->pt_async_rpt.buf[2] = PT_PIP_SENSOR_DATA_REPORT_ID;
/* Reserved */
cd->pt_async_rpt.buf[3] = 0x00;
/* Pad zeros between each set of data points */
for (i = 0; i < 3; i++)
memcpy(&data_buf[PIP1_SENSOR_DATA_MODE_DP_COUNT * 2 * i],
&cd->pt_async_rpt.buf[10 + dp_count * 2 * i],
dp_count * 2);
memcpy(&cd->pt_async_rpt.buf[SENSOR_HEADER_SIZE], data_buf,
PIP1_SENSOR_DATA_MODE_DP_COUNT * 6);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: move_hid_sdm
*
* SUMMARY: Print the raw sensor data into the tthe_tuner sysfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int move_hid_sdm(struct pt_core_data *cd)
{
u8 mrpt = 0;
int pip1_pl_len;
int rc = 0;
int length;
u8 cmd_id = cd->pt_async_rpt.cmd_id;
if (cmd_id != HID_CMD_ID_START_SENSOR_DATA_MODE) {
pt_debug(cd->dev, DL_ERROR,
"%s: Cmd ID error. cmd_id=0x%02X\n",
__func__, cmd_id);
rc = -EINVAL;
goto exit;
}
length = cd->hid_core.hid_max_input_len;
/*
* HID over USB does not require the two byte length field, so
* this should print from input_buf[2] but to keep both finger
* and pen reports the same the length is included
*/
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), length - 2,
"HID-USB-SDM=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_I2C)
tthe_print(cd, cd->input_buf, length,
"HID-I2C-SDM=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP ||
cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) {
mrpt = cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x01;
pt_debug(cd->dev, DL_DEBUG, "%s: mrpt=%d\n",
__func__, mrpt);
rc = stitch_hid_async_report(cd);
if (rc || mrpt)
goto exit;
rc = rebuild_hid_sdm_to_pip1(cd);
if (rc)
goto exit;
pip1_pl_len =
get_unaligned_le16(&cd->pt_async_rpt.buf[0]);
tthe_print(cd, cd->pt_async_rpt.buf,
pip1_pl_len, "sensor_monitor=");
}
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: move_hid_thm
*
* SUMMARY: Print the Tracking Heatmap data into the tthe_tuner sysfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int move_hid_thm(struct pt_core_data *cd)
{
u8 mrpt = 0;
int pip1_pl_len;
int rc = 0;
int length;
u8 cmd_id = cd->pt_async_rpt.cmd_id;
u8 data_buf[PT_MAX_PIP1_MSG_SIZE];
if (cmd_id != HID_CMD_ID_START_TRACKING_HEATMAP_MODE) {
pt_debug(cd->dev, DL_ERROR,
"%s: Cmd ID error. cmd_id=0x%02X\n",
__func__, cmd_id);
rc = -EINVAL;
goto exit;
}
length = cd->hid_core.hid_max_input_len;
/*
* HID over USB does not require the two byte length field, so
* this should print from input_buf[2] but to keep both finger
* and pen reports the same the length is included
*/
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), length - 2,
"HID-USB-THM=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_I2C)
tthe_print(cd, cd->input_buf, length,
"HID-I2C-THM=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP ||
cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) {
mrpt = cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x01;
pt_debug(cd->dev, DL_DEBUG, "%s: mrpt=%d\n",
__func__, mrpt);
rc = stitch_hid_async_report(cd);
if (rc || mrpt)
goto exit;
memset(data_buf, 0, sizeof(data_buf));
/*
* PIP1 payload length is calculated by:
* ( HID Payload Length -
* Timestamp (2) -
* CRC(2))
*/
pip1_pl_len = get_unaligned_le16(
&cd->pt_async_rpt.buf[HID_RESP_PAYLOAD_LEN_OFFSET])
- 4;
memcpy(data_buf,
&cd->pt_async_rpt.buf[HID_RESP_SENSOR_DATA_OFFSET],
pip1_pl_len - SENSOR_HEADER_SIZE);
memset(cd->pt_async_rpt.buf, 0, sizeof(cd->pt_async_rpt.buf));
/* Set Length Field */
cd->pt_async_rpt.buf[0] = LOW_BYTE(PIP1_TRACKING_HEATMAP_SIZE);
cd->pt_async_rpt.buf[1] = HI_BYTE(PIP1_TRACKING_HEATMAP_SIZE);
/* Report ID */
cd->pt_async_rpt.buf[2] = PT_PIP_TRACKING_HEATMAP_REPORT_ID;
/* Reserved */
cd->pt_async_rpt.buf[3] = 0x00;
memcpy(&cd->pt_async_rpt.buf[SENSOR_HEADER_SIZE], data_buf,
pip1_pl_len - SENSOR_HEADER_SIZE);
tthe_print(cd, cd->pt_async_rpt.buf,
PIP1_TRACKING_HEATMAP_SIZE, "THM=");
}
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: move_hid_rtsd
*
* SUMMARY: Print the Realtime Signal Data into the tthe_tuner sysfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int move_hid_rtsd(struct pt_core_data *cd)
{
u8 mrpt = 0;
int rc = 0;
int length;
u8 cmd_id = cd->pt_async_rpt.cmd_id;
if (cmd_id != HID_CMD_ID_REALTIME_SIGNAL_MODE) {
pt_debug(cd->dev, DL_ERROR,
"%s: Cmd ID error. cmd_id=0x%02X\n",
__func__, cmd_id);
rc = -EINVAL;
goto exit;
}
length = cd->hid_core.hid_max_input_len;
/*
* HID over USB does not require the two byte length field, so
* this should print from input_buf[2] but to keep both finger
* and pen reports the same the length is included
*/
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), length - 2,
"HID-USB-RTSD=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_I2C)
tthe_print(cd, cd->input_buf, length,
"HID-I2C-RTSD=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP ||
cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) {
mrpt = cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x01;
pt_debug(cd->dev, DL_DEBUG, "%s: mrpt=%d\n",
__func__, mrpt);
rc = stitch_hid_async_report(cd);
if (rc || mrpt)
goto exit;
tthe_print(cd, cd->pt_async_rpt.buf, length, "RTSD=");
}
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: move_hid_async_sensor_data
*
* SUMMARY: If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
* the tthe_tuner sysfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_hid_async_sensor_data(struct pt_core_data *cd)
{
u8 mrpt = 0;
u8 frpt = 0;
int rc = 0;
u8 cmd_id;
mrpt = cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x01;
frpt = (cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x02) >> 1;
cd->pt_async_rpt.mrpt = mrpt;
cd->pt_async_rpt.frpt = frpt;
if (frpt) {
cmd_id = cd->input_buf[HID_RESP_CMD_ID_OFFSET] & 0x7F;
cd->pt_async_rpt.cmd_id = cmd_id;
} else
cmd_id = cd->pt_async_rpt.cmd_id;
pt_debug(cd->dev, DL_INFO, "%s: Cmd[0x%02X], frpt=%d, mrpt=%d\n",
__func__, cmd_id, frpt, mrpt);
#ifdef TTHE_TUNER_SUPPORT
if (cmd_id == HID_CMD_ID_START_SENSOR_DATA_MODE)
rc = move_hid_sdm(cd);
else if (cmd_id == HID_CMD_ID_START_TRACKING_HEATMAP_MODE)
rc = move_hid_thm(cd);
else if (cmd_id == HID_CMD_ID_REALTIME_SIGNAL_MODE)
rc = move_hid_rtsd(cd);
else {
pt_debug(cd->dev, DL_DEBUG,
"%s: Wrong command id [0x%02X]\n",
__func__, cmd_id);
rc = -EINVAL;
}
#endif
return rc;
}
/*******************************************************************************
* FUNCTION: move_button_data
*
* SUMMARY: Move the valid button data from the input buffer into the system
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw button data into
* the tthe_tuner sysfs node under the label "OpModeData"
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_button_data(struct pt_core_data *cd,
struct pt_sysinfo *si)
{
#ifdef TTHE_TUNER_SUPPORT
int size = get_unaligned_le16(&cd->input_buf[0]);
if (size)
tthe_print(cd, cd->input_buf, size, "OpModeData=");
#endif
memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE,
"xy_mode");
memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE],
BTN_REPORT_SIZE);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, BTN_REPORT_SIZE,
"xy_data");
return 0;
}
/*******************************************************************************
* FUNCTION: move_touch_data_pip
*
* SUMMARY: Move the valid touch data from the input buffer into the system
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw touch data into
* the tthe_tuner sysfs node under the label "OpModeData"
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_touch_data_pip(struct pt_core_data *cd, struct pt_sysinfo *si)
{
int max_tch = si->sensing_conf_data.max_tch;
int num_cur_tch = 0;
int length;
int actual_tch_num;
struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
int size = get_unaligned_le16(&cd->input_buf[0]);
#ifdef TTHE_TUNER_SUPPORT
if (size > 0)
tthe_print(cd, cd->input_buf, size, "OpModeData=");
#endif
memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size);
pt_pr_buf(cd->dev, DL_DEBUG, (u8 *)si->xy_mode, si->desc.tch_header_size, "xy_mode");
pt_get_touch_field(cd->dev, &num_cur_tch, tch->size,
tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs);
if (unlikely(num_cur_tch > max_tch))
num_cur_tch = max_tch;
actual_tch_num = (size - si->desc.tch_header_size)
/ si->desc.tch_record_size;
if (actual_tch_num < num_cur_tch) {
pt_debug(cd->dev, DL_ERROR,
"%s: ATM - Malformed touch packet. actual_tch_num=%d, num_cur_tch=%d\n",
__func__, actual_tch_num, num_cur_tch);
num_cur_tch = actual_tch_num;
}
length = num_cur_tch * si->desc.tch_record_size;
memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length);
pt_pr_buf(cd->dev, DL_DEBUG, (u8 *)si->xy_data, length, "xy_data");
return 0;
}
/*******************************************************************************
* FUNCTION: move_touch_data_hid
*
* SUMMARY: Move the valid touch data from the input buffer into the system
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw touch data into
* the tthe_tuner sysfs node under the label "HID-USB", "HID-I2C" or
* "OpModeData"
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_touch_data_hid(struct pt_core_data *cd,
struct pt_sysinfo *si)
{
int max_tch = si->sensing_conf_data.max_tch;
int num_cur_tch = 0;
u16 hdr_sz;
u16 rec_sz;
int max_tch_per_packet;
static u8 remain_tch;
static u8 packet_no;
static u8 input_sz;
int length;
int rc = 0;
struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
int size = get_unaligned_le16(&cd->input_buf[0]);
hdr_sz = si->desc.tch_header_size;
rec_sz = si->desc.tch_record_size;
max_tch_per_packet = si->desc.max_tch_per_packet;
#ifdef TTHE_TUNER_SUPPORT
length = hdr_sz + rec_sz * max_tch_per_packet;
pt_debug(cd->dev, DL_DEBUG,
"%s: touch report size=%d, len=%d, record=%d, max_tch=%d\n",
__func__, size, length, rec_sz, max_tch_per_packet);
/*
* HID over USB does not require the two byte length field, so
* this should print from input_buf[2] but to keep both finger
* and pen reports the same the length is included
*/
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), length - 2,
"HID-USB-TOUCH=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_I2C)
tthe_print(cd, &(cd->input_buf[0]), length,
"HID-I2C-TOUCH=");
#endif
/*
* The fifth parameter points to the location of "Number of Records":
* Pointer "xy_mode" points to start address of touch report header.
* The byte offset of "Number of Records" is 2 (tch->ofs, retrieved
* from report descriptor). Then the pointer "xy_mode + 3 (skip the
* two bytes length and 1 byte report ID) + tch->ofs" points to the
* location of "Number of Records".
*/
pt_get_touch_field(cd->dev, &num_cur_tch, tch->size,
tch->max, cd->input_buf + 3 + tch->ofs, tch->bofs);
if (unlikely(num_cur_tch > max_tch))
num_cur_tch = max_tch;
/* According to Hybrid Mode touch report defined by Microsoft,
* if the touch number exceeds the max touch per packet (defined
* in report descriptor), the touch packet will be broken up into
* to multiple reports. Timestamp will be consistent across all
* touches in a single frame. The "Number of records" will have the
* total in the first report and be 0 in all subsequent reports that
* belong to the same frame.
*/
pt_debug(cd->dev, DL_INFO, "%s: max_tch=%d, packet_no=%d, num_cur_tch=%d\n",
__func__, max_tch, packet_no, num_cur_tch);
if (packet_no == 0) {
input_sz = hdr_sz + num_cur_tch * rec_sz;
memset(cd->touch_buf, 0, sizeof(cd->touch_buf));
memcpy(cd->touch_buf, cd->input_buf, input_sz);
if (num_cur_tch > max_tch_per_packet) {
remain_tch = num_cur_tch - max_tch_per_packet;
packet_no++;
rc = -EINVAL;
goto exit;
}
} else {
if (remain_tch <= max_tch_per_packet) {
memcpy(&cd->touch_buf[hdr_sz +
max_tch_per_packet * packet_no * rec_sz],
&(cd->input_buf[hdr_sz]),
rec_sz * remain_tch);
remain_tch = 0;
packet_no = 0;
rc = 0;
} else {
memcpy(&cd->touch_buf[hdr_sz +
max_tch_per_packet * packet_no * rec_sz],
&(cd->input_buf[hdr_sz]),
rec_sz * max_tch_per_packet);
remain_tch -= max_tch_per_packet;
packet_no++;
rc = -EINVAL;
goto exit;
}
}
#ifdef TTHE_TUNER_SUPPORT
/* Update pip packet length */
cd->touch_buf[0] = input_sz & 0xff;
cd->touch_buf[1] = (input_sz & 0xff00) >> 8;
/*
* For PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP and
* PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP mode, all touches
* should be combined into a single row for the tthe_tuner node.
*/
if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP ||
cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP)
tthe_print(cd, cd->touch_buf, input_sz,
"OpModeData=");
#endif
memcpy(si->xy_mode, cd->touch_buf, hdr_sz);
pt_pr_buf(cd->dev, DL_DEBUG, (u8 *)si->xy_mode, hdr_sz, "xy_mode");
memcpy(si->xy_data, &cd->touch_buf[hdr_sz], input_sz - hdr_sz);
pt_pr_buf(cd->dev, DL_DEBUG, (u8 *)si->xy_data, input_sz - hdr_sz, "xy_data");
input_sz = 0;
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: move_hid_pen_data
*
* SUMMARY: TODO Move the valid pen data from the input buffer into the system
* system information structure, xy_mode and xy_data.
* - If TTHE_TUNER_SUPPORT is defined print the raw pen data into
* the tthe_tuner sysfs node under the label "HID" starting with the
* report ID.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* *si - pointer to the system information structure
******************************************************************************/
static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si)
{
int size = get_unaligned_le16(&cd->input_buf[0]);
#ifdef TTHE_TUNER_SUPPORT
enum pt_pen_abs abs;
struct pt_pen pen;
int packet_len = 17;
int report_id = 1;
static int report_counter;
int event_id = 2;
int touch_id = 0;
int i = 0;
if (size) {
/*
* HID over USB does not require the two byte length field, so
* this should print from input_buf[2] but to keep both finger
* and pen reports the same the length is included
*/
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), size - 2,
"HID-USB-TOUCH=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_I2C ||
cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP)
tthe_print(cd, &(cd->input_buf[0]), size,
"HID-I2C-TOUCH=");
else if (cd->tthe_hid_usb_format ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) {
memset(cd->touch_buf, 0, sizeof(cd->touch_buf));
for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) {
if (!si->pen_abs[abs].report)
continue;
pt_get_touch_field(cd->dev, &pen.abs[abs],
si->pen_abs[abs].size,
si->pen_abs[abs].max,
cd->input_buf + 3 +
si->pen_abs[abs].ofs,
si->pen_abs[abs].bofs);
pt_debug(cd->dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
__func__, pt_pen_abs_string[abs],
pen.abs[abs], pen.abs[abs]);
}
/* pip packet length: 17 */
cd->touch_buf[i++] = packet_len & 0xff;
cd->touch_buf[i++] = (packet_len & 0xff00) >> 8;
/* pip finger report id: 1*/
cd->touch_buf[i++] = report_id;
/* Timestamp: 0 */
cd->touch_buf[i++] = 0;
cd->touch_buf[i++] = 0;
/* LO: 0; Number of Records: 1*/
cd->touch_buf[i++] = 1;
/* Report Counter:[0-3]; Noise Effects:0 */
cd->touch_buf[i++] = (report_counter & 0x03) << 6;
/* Touch Type: Stylus (2)*/
cd->touch_buf[i++] = PT_OBJ_STYLUS;
/* Tip: pen tip switch; Event ID: 2; Touch ID: 0 */
cd->touch_buf[i++] =
((pen.abs[PT_PEN_TS] & 0x01) << 7) |
((event_id & 0x03) << 5) |
(touch_id & 0x1f);
/* X: Pen X */
cd->touch_buf[i++] = pen.abs[PT_PEN_X] & 0xff;
cd->touch_buf[i++] = (pen.abs[PT_PEN_X] & 0xff00) >> 8;
/* Y: Pen Y */
cd->touch_buf[i++] = pen.abs[PT_PEN_Y] & 0xff;
cd->touch_buf[i++] = (pen.abs[PT_PEN_Y] & 0xff00) >> 8;
/* Pressure: Pen pressure drop the least 4 bits */
cd->touch_buf[i++] = (pen.abs[PT_PEN_P] & 0xff0) >> 4;
/* Major: Pen Tilt_X*/
cd->touch_buf[i++] = pen.abs[PT_PEN_X_TILT] & 0xff;
/* Minor: Pen Tilt_Y*/
cd->touch_buf[i++] = pen.abs[PT_PEN_Y_TILT] & 0xff;
/* Orientation: Btn2, Btn1*/
cd->touch_buf[i++] =
(pen.abs[PT_PEN_BS] & 0x01) |
((pen.abs[PT_PEN_2ND_BS] & 0x01) << 1);
if (++report_counter > 3)
report_counter = 0;
tthe_print(cd, cd->touch_buf, packet_len,
"OpModeData=");
}
}
#endif
memcpy(si->xy_mode, cd->input_buf, HID_PEN_INPUT_HEADER_SIZE);
memcpy(si->xy_data, cd->input_buf, size);
pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen");
return 0;
}
/*******************************************************************************
* FUNCTION: parse_touch_input
*
* SUMMARY: Parse the touch report and take action based on the touch
* report_id.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* size - size of touch record
******************************************************************************/
static int parse_touch_input(struct pt_core_data *cd, int size)
{
struct pt_sysinfo *si = &cd->sysinfo;
int report_id = cd->input_buf[2];
int rc = -EINVAL;
pt_debug(cd->dev, DL_DEBUG, "%s: Received touch report\n",
__func__);
if (!si->ready) {
pt_debug(cd->dev, DL_ERROR,
"%s: Need system information to parse touches\n",
__func__);
return 0;
}
if (!si->xy_mode || !si->xy_data)
return rc;
if (report_id == PT_PIP_TOUCH_REPORT_ID ||
report_id == PT_HID_VS_FINGER_REPORT_ID) {
if (cd->protocol_mode == PT_PROTOCOL_MODE_PIP)
rc = move_touch_data_pip(cd, si);
else {
rc = move_touch_data_hid(cd, si);
if (rc) {
pt_debug(cd->dev, DL_INFO,
"%s: Skip report for the first touch packet\n",
__func__);
return 0;
}
}
} else if ((report_id == PT_HID_PEN_REPORT_ID ||
report_id == PT_HID_VS_PEN_REPORT_ID) &&
cd->protocol_mode != PT_PROTOCOL_MODE_PIP)
rc = move_hid_pen_data(cd, si);
else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID)
rc = move_button_data(cd, si);
else if (report_id == PT_PIP_SENSOR_DATA_REPORT_ID)
rc = move_sensor_data(cd, si);
else if (report_id == PT_PIP_TRACKING_HEATMAP_REPORT_ID)
rc = move_tracking_heatmap_data(cd, si);
else if (report_id == PT_HID_ASYNC_SENSOR_DATA_REPORT_ID)
rc = move_hid_async_sensor_data(cd);
if (rc)
return rc;
/* attention IRQ */
call_atten_cb(cd, PT_ATTEN_IRQ, cd->mode);
return 0;
}
/*******************************************************************************
* FUNCTION: parse_command_input
*
* SUMMARY: Move the response data from the input buffer to the response buffer
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
* size - size of response data
******************************************************************************/
static int parse_command_input(struct pt_core_data *cd, int size)
{
pt_debug(cd->dev, DL_DEBUG, "%s: Received cmd interrupt\n",
__func__);
memcpy(cd->response_buf, cd->input_buf, size);
#if defined(TTHE_TUNER_SUPPORT) && defined(TTDL_DIAGNOSTICS)
if (size && cd->show_tt_data) {
if (cd->pip2_prot_active)
tthe_print(cd, cd->input_buf, size, "TT_DATA_PIP2=");
else
tthe_print(cd, cd->input_buf, size, "TT_DATA=");
}
#endif
mutex_lock(&cd->system_lock);
cd->hid_cmd_state = 0;
mutex_unlock(&cd->system_lock);
wake_up(&cd->wait_q);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_allow_enumeration
*
* SUMMARY: Determine if an enumeration or fully re-probe should perform when
* FW sentinel is seen.
*
* RETURN:
* true = allow enumeration or fully re-probe
* false = skip enumeration and fully re-probe
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static inline bool pt_allow_enumeration(struct pt_core_data *cd)
{
if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
(!cd->hid_reset_cmd_state) &&
(cd->core_probe_complete) &&
(cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
(cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
(cd->hid_cmd_state != HID_CMD_ID_SWITCH_IMAGE + 1) &&
(cd->hid_cmd_state != HID_CMD_ID_START_BOOTLOADER + 1) &&
(cd->mode == PT_MODE_OPERATIONAL)) {
return true;
}
if ((!cd->hid_reset_cmd_state) &&
(cd->core_probe_complete) &&
(cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
(cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
(cd->hid_cmd_state != HID_CMD_ID_SWITCH_IMAGE + 1) &&
(cd->hid_cmd_state != HID_CMD_ID_START_BOOTLOADER + 1) &&
(cd->active_dut_generation != DUT_PIP1_ONLY)) {
return true;
}
pt_debug(cd->dev, DL_INFO,
"%s: Dissallow - %s=%d %s=%d %s=0x%02X %s=%d\n",
__func__,
"hid_reset_cmd_state(0)", cd->hid_reset_cmd_state,
"core_probe_complete(1)", cd->core_probe_complete,
"hid_cmd_state(Not 0x02 or 0x39)", cd->hid_cmd_state,
"active_dut_gen(0,2)", cd->active_dut_generation);
return false;
}
/*******************************************************************************
* FUNCTION: pt_is_touch_report
*
* SUMMARY: Determine if a report ID should be treated as a touch report
*
* RETURN:
* true = report ID is a touch report
* false = report ID is not a touch report
*
* PARAMETERS:
* id - Report ID
******************************************************************************/
static bool pt_is_touch_report(int id)
{
if (id == PT_PIP_TOUCH_REPORT_ID ||
id == PT_HID_PEN_REPORT_ID ||
id == PT_HID_VS_FINGER_REPORT_ID ||
id == PT_HID_VS_PEN_REPORT_ID ||
id == PT_PIP_CAPSENSE_BTN_REPORT_ID ||
id == PT_PIP_SENSOR_DATA_REPORT_ID ||
id == PT_PIP_TRACKING_HEATMAP_REPORT_ID ||
id == PT_HID_ASYNC_SENSOR_DATA_REPORT_ID)
return true;
else
return false;
}
/*******************************************************************************
* FUNCTION: pt_parse_hid_report_to_pip
*
* SUMMARY: Parse the input report of HID SET_REPORT command to normal
* PIP1/PIP2 response and overwrite it to cd->input_buf.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* *remain_packets - pointer to store how many packets left to be received
* *report_type - pointer to store report type
******************************************************************************/
static int pt_parse_hid_report_to_pip(struct pt_core_data *cd,
u16 *remain_packets, int *report_type)
{
int rc = 0;
int cmd_id = 0;
u16 size = get_unaligned_le16(&cd->input_buf[0]);
u16 hid_buf_index = 0;
u16 packet_len = 0;
u16 pip_index = 0;
u16 calc_crc = 0;
u16 resp_crc = 0;
u16 calc_crc_ofs = 0;
u16 cur_payload_len = 0;
u16 actual_payload_len = 0;
static int hid_pl_len;
u8 rsp_bit = 0;
u8 mrpt = 0;
u8 frpt = 0;
u8 status = 0;
u16 pkt_num = 0;
enum pt_report_type cur_report_type = UNKNOWN_REPORT;
mrpt = cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x01;
frpt = (cd->input_buf[HID_RESP_MRPT_OFFSET] & 0x02) >> 1;
/* Should be first packet */
if (frpt) {
if (cd->pt_hid_buf_op.offset) {
pt_debug(cd->dev, DL_WARN,
"%s: Unexpected start of new report detected, previous report discarded\n",
__func__);
/* Clear structure pt_hid_report_buf_op */
memset(&cd->pt_hid_buf_op, 0,
sizeof(struct pt_hid_report_buf_op));
}
hid_pl_len = get_unaligned_le16(
&cd->input_buf[HID_RESP_PAYLOAD_LEN_OFFSET]);
/* Both PIP1 & PIP2 cmd ID mask are 0x7F */
cmd_id = cd->input_buf[HID_RESP_CMD_ID_OFFSET] & 0x7F;
/* Confirm report_type */
if (cd->pt_hid_buf_op.report_type != UNKNOWN_REPORT)
cur_report_type = cd->pt_hid_buf_op.report_type;
else {
if (cd->force_pip3_report_type)
cd->report_type = PIP3_CMD_REPORT;
cur_report_type = cd->report_type;
}
} else {
if (cd->pt_hid_buf_op.offset == 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Start of the report was not detected. Current report discarded\n",
__func__);
rc = -EBADMSG;
goto exit;
}
/* 'report_type' only can be confirmed by the first packet */
cur_report_type = cd->pt_hid_buf_op.report_type;
}
pt_debug(cd->dev, DL_DEBUG,
"%s: hid report type=%d, frpt_bit=%d, mrpt_bit=%d\n",
__func__, cur_report_type, frpt, mrpt);
/* Bounds Checking */
if ((size + cd->pt_hid_buf_op.offset) <= HID_BUF_OP_BUFFER_SIZE) {
memcpy(cd->pt_hid_buf_op.buf + cd->pt_hid_buf_op.offset,
&cd->input_buf[0], size);
cd->pt_hid_buf_op.offset += size;
} else {
pt_debug(cd->dev, DL_ERROR,
"%s: Overflow! Packet size=%d, buf_offset=%d, buf_size=%d\n",
__func__, size, cd->pt_hid_buf_op.offset,
HID_BUF_OP_BUFFER_SIZE);
rc = -ENOMEM;
goto exit;
}
pt_debug(cd->dev, DL_DEBUG,
"%s: Packet size=%d, buf_offset=%d, buf_size=%d\n",
__func__, size, cd->pt_hid_buf_op.offset,
HID_BUF_OP_BUFFER_SIZE);
/* Last packet/Only one packet */
if (mrpt == 0) {
/* Total payload length */
cur_payload_len = get_unaligned_le16(
&cd->pt_hid_buf_op.buf[HID_RESP_PAYLOAD_LEN_OFFSET]);
/* Parse HID Report and compose PIP response */
memset(cd->input_buf, 0, sizeof(cd->input_buf));
hid_buf_index = 0; /* Initial offset of pt_hid_buf_op */
/*
* Confirm the initial offset(pip_index) of PIP response in
* 'input_buf', according to the spec 001-30009, 1 bytes offset
* for PIP1 message to rebuild:
* [ PIP1 ] [ PIP1 Embedded in HID ]
* 1 byte Length field(LSB) (Null)
*----------------------------------------------< offset
* 1 byte Length field(MSB) 1 byte length of payload(LSB)
* 1 byte REPORT_ID 1 byte length of payload(MSB)
* 1 byte RSVD <= 1 byte SEQ
* 1 byte command code 1 byte command code
* N byte Parameter Data N byte Parameter Data
* (Removed) 2 byte CRC
*
* No offset is needed for PIP2 message to rebuild:
* [ PIP2 ] [ PIP2 Embedded in HID ]
*----------------------------------------------< offset
* 2 byte Length field 2 byte length of payload
* 1 byte SEQ 1 byte SEQ
* 1 byte CMD ID <= 1 byte CMD ID
* N byte Report Body N byte Report Body
* 2 byte CRC 2 byte CRC2 byte CRC
*/
if (cur_report_type == PIP1_CMD_REPORT)
pip_index = 1;
else
pip_index = 0;
calc_crc_ofs = pip_index;
do {
packet_len = get_unaligned_le16(
&cd->pt_hid_buf_op.buf[hid_buf_index]);
if (((hid_buf_index + packet_len) >
cd->pt_hid_buf_op.offset) ||
(packet_len <= HID_RESP_HEADER_SIZE)) {
rc = -EBADMSG;
goto exit;
}
/*
* 'Length of payload' is the entire payload. If the
* payload is greater than (wMaxInputLength - 7) and
* broken up across multiple reports.
*/
if ((packet_len - HID_RESP_HEADER_SIZE) <
cur_payload_len)
actual_payload_len =
packet_len - HID_RESP_HEADER_SIZE;
else
actual_payload_len = cur_payload_len;
memcpy(&cd->input_buf[pip_index],
&cd->pt_hid_buf_op.buf[hid_buf_index +
HID_RESP_HEADER_SIZE],
actual_payload_len);
hid_buf_index += packet_len;
pip_index += actual_payload_len;
cur_payload_len -= actual_payload_len;
} while (hid_buf_index < cd->pt_hid_buf_op.offset);
/* Verify HID response CRC */
calc_crc = crc_ccitt_calculate(&cd->input_buf[calc_crc_ofs],
pip_index - calc_crc_ofs - PT_CRC_LEN);
resp_crc = cd->input_buf[pip_index - 2] << 8;
resp_crc |= cd->input_buf[pip_index - 1];
if (resp_crc != calc_crc) {
pt_debug(cd->dev, DL_WARN,
"%s: CRC ERR: calc_crc=%04X, resp_crc=%04X\n",
__func__, calc_crc, resp_crc);
rc = -EBADMSG;
goto exit;
}
pt_pr_buf(cd->dev, DL_DEBUG, cd->pt_hid_buf_op.buf,
cd->pt_hid_buf_op.offset, "<<< ATM - HID buf");
status = cd->pt_hid_buf_op.buf[HID_RESP_STATUS_OFFSET];
if (status)
pt_debug(cd->dev, DL_ERROR,
"%s: Error in PIP3 Cmd[0x%02X], status=%d\n",
__func__, cmd_id, status);
if (cur_report_type == PIP1_CMD_REPORT) {
/* Set Length Field: Remove CRC field(2 bytes) */
pip_index -= 2;
cd->input_buf[0] = LOW_BYTE(pip_index);
cd->input_buf[1] = HI_BYTE(pip_index);
/* Add Report ID */
cd->input_buf[2] = PT_PIP_NON_HID_RESPONSE_ID;
/* Overwrite SEQ with RSVD(0x00) */
cd->input_buf[3] = 0x00;
/* Get PIP cmd id from HID cmd id */
cmd_id = cd->input_buf[4] & 0x7f;
cd->input_buf[4] = pt_get_pip_cmd_id_from_hid(
cmd_id, PIP1_TO_HID_CMD_ID_MAP);
pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, pip_index,
"<<< ATM - PIP1 buf");
} else if (cur_report_type == PIP2_CMD_REPORT) {
/* Get PIP cmd_id from HID cmd_id */
cmd_id = cd->input_buf[3] & 0x7f;
rsp_bit = cd->input_buf[3] & 0x80;
cd->input_buf[3] = pt_get_pip_cmd_id_from_hid(
cmd_id, PIP2_TO_HID_CMD_ID_MAP);
cd->input_buf[3] |= rsp_bit;
/* Update CRC(MSB) */
calc_crc =
crc_ccitt_calculate(cd->input_buf, pip_index - 2);
cd->input_buf[pip_index - 2] = HI_BYTE(calc_crc);
cd->input_buf[pip_index - 1] = LOW_BYTE(calc_crc);
pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, pip_index,
"<<< ATM - PIP2 buf");
} else if (cur_report_type == PIP3_CMD_REPORT) {
pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, pip_index,
"<<< ATM - PIP3 payload buf");
}
} else {
cd->pt_hid_buf_op.report_type = cur_report_type;
cd->pt_hid_buf_op.last_remain_packet += 1;
pkt_num = cd->pt_hid_buf_op.last_remain_packet;
if ((cd->pt_hid_buf_op.offset - pkt_num * HID_RESP_HEADER_SIZE)
>= hid_pl_len) {
pt_debug(cd->dev, DL_ERROR,
"%s: Extra report detected, stitched size greater than payload length\n",
__func__);
rc = -EBADMSG;
goto exit;
}
}
exit:
if (mrpt == 0 || rc) {
/* Clear structure pt_hid_report_buf_op */
memset(&cd->pt_hid_buf_op, 0,
sizeof(struct pt_hid_report_buf_op));
}
if (remain_packets)
*remain_packets = mrpt;
if (report_type)
*report_type = cur_report_type;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_parse_input
*
* SUMMARY: Parse the input data read from DUT due to IRQ. Handle data based
* on if its a response to a command or asynchronous touch data / reset
* sentinel. PIP2.x messages have additional error checking that is
* parsed (SEQ match from cmd to rsp, CRC valid).
* Look for special packets based on unique lengths:
* 0 bytes - APP(FW) reset sentinel or Gen5/6 BL sentinel
* 2 bytes - Empty buffer (PIP 1.7 and earlier)
* 11 bytes - possible PIP2.x reset sentinel (TAG and SEQ must = 0)
* 0xFFXX - Empty buffer (PIP 1.7+)
* Queue a startup after any asynchronous FW reset sentinel is seen, unless
* the initial probe has not yet been done.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_parse_input(struct pt_core_data *cd)
{
int rc = 0;
int report_id = 0;
int tag_seq = 0;
int cmd_id = 0;
int is_command = 0;
int size = 0;
int report_type = UNKNOWN_REPORT;
bool touch_report = true;
unsigned short remain_packets = 0;
unsigned short calc_crc;
unsigned short resp_crc;
cd->fw_sys_mode_in_standby_state = false;
size = get_unaligned_le16(&cd->input_buf[0]);
pt_debug(cd->dev, DL_DEBUG, "<<< %s: IRQ Triggered, read len [%d]\n",
__func__, size);
if (size <= PT_MAX_INPUT)
pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, size,
"<<< Read buf");
if (size == 0 || IS_BL_SENTINEL(cd, size)) {
touch_report = false;
cd->hw_detected = true;
cd->bl_pip_ver_ready = false;
cd->app_pip_ver_ready = false;
if (size == 0) {
mutex_lock(&cd->system_lock);
cd->pip2_prot_active = false;
if (cd->active_dut_generation == DUT_PIP1_ONLY) {
/*
* For Gen5/6 this sentinel could be from
* the BL or FW. Attempt to set the correct
* mode based on the previous PIP command.
*/
if (cd->hid_cmd_state ==
PIP1_BL_CMD_ID_LAUNCH_APP + 1) {
cd->mode = PT_MODE_OPERATIONAL;
cd->enum_status =
ENUM_STATUS_FW_RESET_SENTINEL;
} else if (cd->hid_cmd_state ==
PIP1_CMD_ID_START_BOOTLOADER + 1 ||
cd->hid_reset_cmd_state) {
cd->mode = PT_MODE_BOOTLOADER;
cd->enum_status =
ENUM_STATUS_BL_RESET_SENTINEL;
} else {
cd->mode = PT_MODE_UNKNOWN;
cd->enum_status =
ENUM_STATUS_START;
}
cd->fw_system_mode = FW_SYS_MODE_UNDEFINED;
pt_debug(cd->dev, DL_INFO,
"%s: ATM Gen5/6 %s sentinel received\n",
__func__,
(cd->mode == PT_MODE_OPERATIONAL ?
"FW" :
(cd->mode == PT_MODE_BOOTLOADER ?
"BL" : "Unknown")));
} else {
cd->mode = PT_MODE_OPERATIONAL;
cd->fw_system_mode = FW_SYS_MODE_BOOT;
cd->enum_status =
ENUM_STATUS_FW_RESET_SENTINEL;
pt_debug(cd->dev, DL_INFO,
"%s: ATM PT/TT FW sentinel received\n",
__func__);
}
mutex_unlock(&cd->system_lock);
if (pt_allow_enumeration(cd)) {
if (cd->active_dut_generation == DUT_UNKNOWN) {
pt_debug(cd->dev, DL_INFO,
"%s: Queue Restart\n", __func__);
pt_queue_restart(cd);
} else {
pt_debug(cd->dev, DL_INFO,
"%s: Queue Enum\n", __func__);
pt_queue_enum(cd);
}
} else {
pt_debug(cd->dev, DL_INFO,
"%s: ATM - Sentinel - No Queued Action\n",
__func__);
/*
* Sentinel "00 00" is expected for SWITCH_IMAGE
* command, so the code in this "if" is needed
* to avoid "timeout" issue after sending
* SWITCH_IMAGE command.
*/
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
cd->hid_cmd_state ==
(HID_CMD_ID_SWITCH_IMAGE + 1)) {
if (cd->img_id == 1) {
mutex_lock(&cd->system_lock);
cd->mode =
PT_MODE_SECONDARY_IMAGE;
mutex_unlock(&cd->system_lock);
}
}
}
} else {
/* Sentinel must be from TT/TC BL */
mutex_lock(&cd->system_lock);
cd->pip2_prot_active = true;
cd->enum_status = ENUM_STATUS_BL_RESET_SENTINEL;
cd->mode = PT_MODE_BOOTLOADER;
cd->sysinfo.ready = false;
mutex_unlock(&cd->system_lock);
pt_debug(cd->dev, DL_INFO,
"%s: BL Reset sentinel received\n", __func__);
if (cd->flashless_dut &&
cd->flashless_auto_bl == PT_ALLOW_AUTO_BL) {
pt_debug(cd->dev, DL_INFO,
"%s: BL to RAM for flashless DUT\n",
__func__);
kthread_run(start_fw_upgrade, cd, "pt_loader");
}
}
mutex_lock(&cd->system_lock);
memcpy(cd->response_buf, cd->input_buf, 2);
if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) {
mutex_unlock(&cd->system_lock);
pt_debug(cd->dev, DL_WARN,
"%s: Device Initiated Reset\n", __func__);
wake_up(&cd->wait_q);
return 0;
}
cd->hid_reset_cmd_state = 0;
if (cd->hid_cmd_state == PIP1_CMD_ID_START_BOOTLOADER + 1 ||
cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 ||
cd->hid_cmd_state == PIP1_CMD_ID_USER_CMD + 1 ||
(cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
(cd->hid_cmd_state == HID_CMD_ID_SWITCH_IMAGE + 1 ||
cd->hid_cmd_state == HID_CMD_ID_START_BOOTLOADER + 1)))
cd->hid_cmd_state = 0;
wake_up(&cd->wait_q);
mutex_unlock(&cd->system_lock);
return 0;
} else if (size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) {
/*
* This debug message below is used by PBATS to calculate the
* time from the last lift off IRQ to when FW exits LFT mode.
*/
touch_report = false;
pt_debug(cd->dev, DL_WARN,
"%s: DUT - Empty buffer detected\n", __func__);
return 0;
} else if (size > PT_MAX_INPUT) {
pt_debug(cd->dev, DL_ERROR,
"%s: DUT - Unexpected len field in active bus data!\n",
__func__);
return -EINVAL;
}
/* Blindly initialize the report_id */
report_id = cd->input_buf[HID_RESP_REPORT_ID_OFFSET];
/* Check if it is HID Wrapper report */
if (PT_IS_HID_WRAP_PIP_REPORT(report_id)) {
#ifdef TTHE_TUNER_SUPPORT
if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB)
tthe_print(cd, &(cd->input_buf[2]), size - 2,
"HID-USB-RSP=");
else
tthe_print(cd, &(cd->input_buf[0]), size,
"HID-I2C-RSP=");
#endif
rc = pt_parse_hid_report_to_pip(cd, &remain_packets,
&report_type);
if (rc || (remain_packets != 0)) {
pt_debug(cd->dev, DL_INFO,
"%s: Parse HID report rc=%d, remain=%d\n",
__func__, rc, remain_packets);
return rc;
}
/*
* Update "report_id" & "size" since input_buf has been
* overwritten, however, PIP3 type only includes the payload
* and so the report_id does not need to be saved.
*/
if (report_type != PIP3_CMD_REPORT)
report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET];
size = get_unaligned_le16(&cd->input_buf[0]);
}
if (cd->pip2_prot_active) {
/*
* Verify response is actually a PIP2 response. PIP2 does
* have a report_id so first decode it as PIP1 to verify if
* the incoming report was actually a touch report. Decoding
* the report_id however, is not enough as a PIP2 response
* could have a valid report_id in the TAG/SEQ byte.
*/
tag_seq = cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F;
calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2);
resp_crc = cd->input_buf[size - 2] << 8;
resp_crc |= cd->input_buf[size - 1];
if ((cd->pip2_cmd_tag_seq != tag_seq) &&
(resp_crc != calc_crc) &&
(pt_is_touch_report(report_id))) {
pt_debug(cd->dev, DL_INFO, "%s: %s 0x%02X %s\n",
__func__, "Received Touch report_id",
report_id, "when expecting a PIP2 report");
touch_report = true;
} else {
/* PIP2 does not have a report id, hard code it */
report_id = 0x00;
is_command = 1;
touch_report = false;
cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET];
}
} else {
cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET];
}
if (report_id == PT_PIP_WAKEUP_REPORT_ID) {
pt_wakeup_host(cd);
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, cd->input_buf, size, "TT_WAKEUP=");
#endif
return 0;
}
mod_timer_pending(&cd->watchdog_timer, jiffies +
msecs_to_jiffies(cd->watchdog_interval));
/*
* If it is PIP2 response, the report_id has been set to 0,
* so it will not be parsed as a touch packet.
*/
if (!pt_is_touch_report(report_id)) {
is_command = 1;
touch_report = false;
}
if (unlikely(is_command)) {
pt_debug(cd->dev, DL_DEBUG,
"%s: pip2=%d report_id=0x%02X, pip_cmd_code=0x%02X\n",
__func__, cd->pip2_prot_active, report_id,
(cmd_id & PIP2_CMD_COMMAND_ID_MASK));
parse_command_input(cd, size);
return 0;
}
if (touch_report)
parse_touch_input(cd, size);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_read_input
*
* SUMMARY: Reads incoming data off of the active bus
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_read_input(struct pt_core_data *cd)
{
struct device *dev = cd->dev;
int rc = 0;
int t;
int retry = PT_BUS_READ_INPUT_RETRY_COUNT;
/*
* Workaround for easywake failure
* Interrupt for easywake, wait for bus controller to wake
*/
mutex_lock(&cd->system_lock);
if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) {
if (cd->sleep_state == SS_SLEEP_ON) {
mutex_unlock(&cd->system_lock);
if (!dev->power.is_suspended)
goto read;
t = wait_event_timeout(cd->wait_q,
(cd->wait_until_wake == 1),
msecs_to_jiffies(2000));
#ifdef TTDL_DIAGNOSTICS
if (IS_TMO(t)) {
cd->bus_transmit_error_count++;
pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
pt_debug(dev, DL_ERROR,
"%s: !!!I2C Transmission Error %d\n",
__func__,
cd->bus_transmit_error_count);
}
#else
if (IS_TMO(t))
pt_queue_enum(cd);
#endif /* TTDL_DIAGNOSTICS */
goto read;
}
}
mutex_unlock(&cd->system_lock);
read:
memset(cd->input_buf, 0, sizeof(cd->input_buf));
/* Try reading up to 'retry' times */
while (retry-- != 0) {
rc = pt_adap_read_default_nosize(cd, cd->input_buf,
PT_MAX_INPUT);
if (!rc) {
pt_debug(dev, DL_DEBUG,
"%s: Read input successfully\n", __func__);
goto read_exit;
}
usleep_range(1000, 2000);
}
pt_debug(dev, DL_ERROR,
"%s: Error getting report, rc=%d\n", __func__, rc);
read_exit:
return rc;
}
static irqreturn_t pt_top_irq(int irq, void *data)
{
struct pt_core_data *cd = data;
cd->timestamp = ktime_get();
return IRQ_WAKE_THREAD;
}
EXPORT_SYMBOL_GPL(pt_top_irq);
/*******************************************************************************
* FUNCTION: pt_bottom_irq
*
* SUMMARY: Process all detected interrupts
*
* RETURN:
* IRQ_HANDLED - Finished processing the interrupt
*
* PARAMETERS:
* irq - IRQ number
* *handle - pointer to core data struct
******************************************************************************/
irqreturn_t pt_bottom_irq(int irq, void *handle)
{
struct pt_core_data *cd = handle;
int rc = 0;
#ifdef TTDL_PTVIRTDUT_SUPPORT
if (!cd->route_bus_virt_dut) {
if (!pt_check_irq_asserted(cd))
return IRQ_HANDLED;
}
#else
if (!pt_check_irq_asserted(cd))
return IRQ_HANDLED;
#endif /* TTDL_PTVIRTDUT_SUPPORT */
rc = pt_read_input(cd);
#ifdef TTDL_DIAGNOSTICS
cd->irq_count++;
/* Used to calculate T-Refresh */
if (cd->t_refresh_active) {
if (cd->t_refresh_count == 0) {
cd->t_refresh_time = jiffies;
cd->t_refresh_count++;
} else if (cd->t_refresh_count < cd->t_refresh_total) {
cd->t_refresh_count++;
} else {
cd->t_refresh_active = 0;
cd->t_refresh_time =
jiffies_to_msecs(jiffies - cd->t_refresh_time);
}
}
#endif /* TTDL_DIAGNOSTICS */
if (!rc)
pt_parse_input(cd);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(pt_bottom_irq);
#ifdef PT_AUX_BRIDGE_ENABLED
/*******************************************************************************
* FUNCTION: pt_trigger_ttdl_irq
*
* SUMMARY: API for other kernel drivers to artificially trigger a TTDL IRQ
*
* IMPROVE - dpmux should likely call pt_get_core_data once and make a
* copy of cd so that the function only needs to be called once.
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS: none
*
******************************************************************************/
int pt_trigger_ttdl_irq(void)
{
struct pt_core_data *cd;
char *core_name = PT_DEFAULT_CORE_ID;
cd = pt_get_core_data(core_name);
if (!cd) {
pr_err("%s: No Device\n", __func__);
return -ENODEV;
}
pt_irq(cd->irq, (void *)cd);
return 0;
}
EXPORT_SYMBOL_GPL(pt_trigger_ttdl_irq);
#endif
/*******************************************************************************
* FUNCTION: _pt_subscribe_attention
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to subscribe themselves into the TTDL attention list
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* type - attention type enum
* *id - ID of the module calling this function
* *func - callback function pointer to be called when notified
* mode - attention mode
******************************************************************************/
int _pt_subscribe_attention(struct device *dev,
enum pt_atten_type type, char *id, int (*func)(struct device *),
int mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct atten_node *atten, *atten_new;
atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL);
if (!atten_new)
return -ENOMEM;
pt_debug(cd->dev, DL_INFO, "%s from '%s'\n", __func__,
dev_name(cd->dev));
spin_lock(&cd->spinlock);
list_for_each_entry(atten, &cd->atten_list[type], node) {
if (atten->id == id && atten->mode == mode) {
spin_unlock(&cd->spinlock);
kfree(atten_new);
pt_debug(cd->dev, DL_INFO, "%s: %s=%p %s=%d\n",
__func__,
"already subscribed attention",
dev, "mode", mode);
return 0;
}
}
atten_new->id = id;
atten_new->dev = dev;
atten_new->mode = mode;
atten_new->func = func;
list_add(&atten_new->node, &cd->atten_list[type]);
spin_unlock(&cd->spinlock);
return 0;
}
/*******************************************************************************
* FUNCTION: _pt_unsubscribe_attention
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to unsubscribe themselves from the TTDL attention list
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* type - attention type enum
* *id - ID of the module calling this function
* *func - function pointer
* mode - attention mode
******************************************************************************/
int _pt_unsubscribe_attention(struct device *dev,
enum pt_atten_type type, char *id, int (*func)(struct device *),
int mode)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
struct atten_node *atten, *atten_n;
spin_lock(&cd->spinlock);
list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) {
if (atten->id == id && atten->mode == mode) {
pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n",
__func__,
"unsub for atten->dev", atten->dev,
"atten->mode", atten->mode);
list_del(&atten->node);
spin_unlock(&cd->spinlock);
kfree(atten);
return 0;
}
}
spin_unlock(&cd->spinlock);
return -ENODEV;
}
/*******************************************************************************
* FUNCTION: _pt_request_exclusive
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request exclusive access
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* timeout_ms - timeout to wait for exclusive access
******************************************************************************/
static int _pt_request_exclusive(struct device *dev,
int timeout_ms)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return request_exclusive(cd, (void *)dev, timeout_ms);
}
/*******************************************************************************
* FUNCTION: _pt_release_exclusive
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to release exclusive access
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int _pt_release_exclusive(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return release_exclusive(cd, (void *)dev);
}
/*******************************************************************************
* FUNCTION: _pt_request_reset
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request the DUT to be reset. Function returns err if refused or
* timeout occurs (Note: core uses fixed timeout period).
*
* NOTE: Function blocks until ISR occurs.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* protect - flag to call protected or non-protected
******************************************************************************/
static int _pt_request_reset(struct device *dev, int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int rc;
rc = pt_dut_reset(cd, protect);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
__func__, rc);
}
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_enum
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request TTDL to queue an enum. This function will return err
* if refused; if no error then enum has completed and system is in
* normal operation mode.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* wait - boolean to determine if to wait for startup event
******************************************************************************/
static int _pt_request_enum(struct device *dev, bool wait)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_queue_enum(cd);
if (wait)
wait_event_timeout(cd->wait_q,
cd->startup_state == STARTUP_DONE,
msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT));
return 0;
}
/*******************************************************************************
* FUNCTION: _pt_request_sysinfo
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request the pointer to the system information structure. This
* function will return NULL if sysinfo has not been acquired from the
* DUT yet.
*
* RETURN: Pointer to the system information struct
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
struct pt_sysinfo *_pt_request_sysinfo(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int rc = 0;
/* Attempt to get sysinfo if not ready and panel_id is from XY pin */
if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
!cd->sysinfo.ready) {
rc = pt_hid_output_get_sysinfo_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error getting sysinfo rc=%d\n",
__func__, rc);
}
}
if (cd->sysinfo.ready)
return &cd->sysinfo;
return NULL;
}
/*******************************************************************************
* FUNCTION: _pt_request_loader_pdata
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request the pointer to the loader platform data
*
* RETURN: Pointer to the loader platform data struct
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static struct pt_loader_platform_data *_pt_request_loader_pdata(
struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
return cd->pdata->loader_pdata;
}
/*******************************************************************************
* FUNCTION: _pt_request_start_wd
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request to start the TTDL watchdog
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int _pt_request_start_wd(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_start_wd_timer(cd);
return 0;
}
/*******************************************************************************
* FUNCTION: _pt_request_stop_wd
*
* SUMMARY: Function pointer included in core_cmds to allow other modules
* to request to stop the TTDL watchdog
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
static int _pt_request_stop_wd(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_stop_wd_timer(cd);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_pip2_launch_app
*
* SUMMARY: Sends the PIP2 EXECUTE command to launch the APP and then wait for
* the FW reset sentinel to indicate the function succeeded.
*
* NOTE: Calling this function when the DUT is in Application mode WILL result
* in a timeout delay and with the DUT being reset with an XRES.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* protect - flag to call protected or non-protected
******************************************************************************/
static int pt_pip2_launch_app(struct device *dev, int protect)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
u16 actual_read_len;
u16 tmp_enum_status = cd->enum_status;
u8 read_buf[12];
u8 status;
int time = 0;
int rc = 0;
mutex_lock(&cd->system_lock);
cd->enum_status = ENUM_STATUS_START;
pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
mutex_unlock(&cd->system_lock);
rc = _pt_request_pip2_send_cmd(dev, protect,
PIP2_CMD_ID_EXECUTE, NULL, 0, read_buf,
&actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: PIP2 EXECUTE cmd failed rc = %d\n",
__func__, rc);
} else {
status = read_buf[PIP2_RESP_STATUS_OFFSET];
/* Test for no or invalid image in FLASH, no point to reset */
if (status == PIP2_RSP_ERR_INVALID_IMAGE) {
rc = status;
goto exit;
}
/* Any other boot failure */
if (status != 0) {
pt_debug(dev, DL_ERROR,
"%s: FW did not EXECUTE, status = %d\n",
__func__, status);
rc = status;
}
}
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to launch APP, XRES DUT rc = %d\n",
__func__, rc);
goto exit;
}
while ((cd->enum_status == ENUM_STATUS_START) && time < 240) {
msleep(20);
pt_debug(cd->dev, DL_INFO, "%s: wait for %d for enum=0x%04X\n",
__func__, time, cd->enum_status);
time += 20;
}
if (cd->enum_status == ENUM_STATUS_START) {
pt_debug(cd->dev, DL_WARN,
"%s: TMO waiting for FW reset sentinel\n", __func__);
rc = -ETIME;
}
exit:
if (cd->enum_status == ENUM_STATUS_START) {
/* Reset to original state because we could be stuck in BL */
mutex_lock(&cd->system_lock);
cd->enum_status = tmp_enum_status;
mutex_unlock(&cd->system_lock);
}
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_request_pip2_launch_app
*
* SUMMARY: Calls pt_pip2_launch_app() when configured to. A small delay is
* inserted to ensure the reset has allowed the BL reset sentinel to be
* consumed.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
******************************************************************************/
static int _pt_request_pip2_launch_app(struct device *dev, int protect)
{
return pt_pip2_launch_app(dev, protect);
}
/*******************************************************************************
* FUNCTION: _pt_request_wait_for_enum_state
*
* SUMMARY: Loops for up to timeout waiting for the startup_status to reach
* the state passed in or STARTUP_STATUS_COMPLETE whichever comes first
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data structure
* timeout - timeout for how long to wait
* state - enum state to wait for
******************************************************************************/
static int _pt_request_wait_for_enum_state(struct device *dev, int timeout,
int state)
{
int rc = 0;
int t;
struct pt_core_data *cd = dev_get_drvdata(dev);
t = wait_event_timeout(cd->wait_q,
(cd->enum_status & state) || (cd->enum_status & 0x0100),
msecs_to_jiffies(timeout));
if (IS_TMO(t)) {
pt_debug(cd->dev, DL_ERROR,
"%s: TMO waiting for enum state 0x%04X in %dms\n",
__func__, state, timeout);
pt_debug(cd->dev, DL_WARN,
"%s: enum state reached 0x%04X\n",
__func__, cd->enum_status);
rc = -ETIME;
} else if (cd->enum_status & state) {
pt_debug(cd->dev, DL_INFO,
"%s: Enum state reached: enum=0x%04X in %dms\n",
__func__, cd->enum_status,
(t == 1) ? timeout : (timeout - jiffies_to_msecs(t)));
} else {
if (t == 1) {
pt_debug(
cd->dev, DL_ERROR,
"%s: TMO waiting for enum state 0x%04X in %dms\n",
__func__, state, timeout);
rc = -ETIME;
} else {
pt_debug(
cd->dev, DL_ERROR,
"%s: Enum state 0x%04X not reached in %dms\n",
__func__, state, timeout - jiffies_to_msecs(t));
rc = -EINVAL;
}
pt_debug(cd->dev, DL_INFO, "%s: enum state reached 0x%04X\n",
__func__, cd->enum_status);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_wake_device_from_deep_sleep_
*
* SUMMARY: Call the set_power function and set the DUT to wake up from
* deep sleep.
*
* RETURN:
* 0 = success
* !0 = error
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_wake_device_from_deep_sleep_(
struct pt_core_data *cd)
{
int rc;
rc = pt_hid_cmd_set_power_(cd, HID_POWER_ON);
if (rc)
rc = -EAGAIN;
/* Prevent failure on sequential wake/sleep requests from OS */
msleep(20);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_wake_device_from_easy_wake_
*
* SUMMARY: Wake up device from Easy-Wake state.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd)
{
mutex_lock(&cd->system_lock);
cd->wait_until_wake = 1;
mutex_unlock(&cd->system_lock);
wake_up(&cd->wait_q);
msleep(20);
return pt_core_wake_device_from_deep_sleep_(cd);
}
/*******************************************************************************
* FUNCTION: pt_restore_parameters_
*
* SUMMARY: This function sends all RAM parameters stored in the linked list
* back to the DUT
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
******************************************************************************/
static int pt_restore_parameters_(struct pt_core_data *cd)
{
struct param_node *param;
int rc = 0;
if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
goto exit;
spin_lock(&cd->spinlock);
list_for_each_entry(param, &cd->param_list, node) {
spin_unlock(&cd->spinlock);
pt_debug(cd->dev, DL_INFO, "%s: Parameter id:%d value:%d\n",
__func__, param->id, param->value);
rc = pt_pip_set_param_(cd, param->id,
param->value, param->size);
if (rc)
goto exit;
spin_lock(&cd->spinlock);
}
spin_unlock(&cd->spinlock);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_exit_bl_
*
* SUMMARY: Attempt to exit the BL and run the application, taking into account
* a DUT that may not have flash and will need FW to be loaded into RAM
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* *status_str - pointer to optional status string buffer
* buf_size - size of status_str buffer
******************************************************************************/
int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size)
{
int rc;
int wait_time = 0;
u8 mode = PT_MODE_UNKNOWN;
bool load_status_str = false;
/* Check and correct "mode" if need when protocol mode is HID */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
rc = _pt_detect_dut_mode(cd->dev, &mode);
if (!rc && cd->mode != mode) {
pt_debug(cd->dev, DL_WARN,
"%s: cd->mode(%d) doesn't match detected(%d)\n",
__func__, cd->mode, mode);
cd->mode = mode;
}
}
/*
* Below function has protective call to ensure no enum is still on
* going, while this kind of protection should be applied widely in
* future (TODO).
*/
rc = pt_pip2_get_mode_sysmode(cd, &mode, NULL);
if (status_str && buf_size >= 50)
load_status_str = true;
if (mode == PT_MODE_BOOTLOADER) {
if (cd->flashless_dut == 1) {
rc = pt_hw_hard_reset(cd);
} else {
rc = pt_pip2_launch_app(cd->dev,
PT_CORE_CMD_UNPROTECTED);
if (rc == PIP2_RSP_ERR_INVALID_IMAGE) {
pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
__func__, "Invalid image in FLASH rc", rc);
} else if (rc) {
pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
__func__, "Failed to launch app rc", rc);
}
}
if (!rc) {
if (cd->flashless_dut == 1) {
/* Wait for BL to complete before enum */
rc = _pt_request_wait_for_enum_state(cd->dev,
4000, ENUM_STATUS_FW_RESET_SENTINEL);
if (rc && load_status_str) {
strcpy(status_str,
"No FW sentinel after BL");
goto exit;
}
}
/*
* If the host wants to interact with the FW or do a
* forced calibration, the FW must be out of BOOT mode
* and the system information must have been retrieved.
* Reaching the FW_OUT_OF_BOOT state guarantees both.
* If, however, the enumeration does not reach this
* point, the DUT may still be in APP mode so test
* for all conditions.
*/
rc = _pt_request_wait_for_enum_state(cd->dev, 4500,
ENUM_STATUS_FW_OUT_OF_BOOT);
if (!rc || cd->enum_status >=
ENUM_STATUS_FW_RESET_SENTINEL) {
mutex_lock(&cd->system_lock);
cd->mode = PT_MODE_OPERATIONAL;
mutex_unlock(&cd->system_lock);
}
if (rc) {
pt_debug(cd->dev, DL_WARN, "%s: %s: 0x%04X\n",
__func__, "Failed to enum with DUT",
cd->enum_status);
if (load_status_str && !(cd->enum_status &
ENUM_STATUS_FW_OUT_OF_BOOT)) {
strcpy(status_str,
"FW Stuck in Boot mode");
goto exit;
}
}
/*
* The comming FW sentinel could wake up the event
* queue, which has chance to be taken by next command
* wrongly. Following delay is a workaround to avoid
* this issue for most situations.
*/
msleep(20);
pt_start_wd_timer(cd);
}
if (load_status_str) {
if (rc == PIP2_RSP_ERR_INVALID_IMAGE)
strcpy(status_str,
"Failed - Invalid image in FLASH");
else if (!rc)
strcpy(status_str,
"Entered APP from BL mode");
else
strcpy(status_str,
"Failed to enter APP from BL mode");
}
} else if (mode == PT_MODE_OPERATIONAL) {
mutex_lock(&cd->system_lock);
cd->mode = mode;
mutex_unlock(&cd->system_lock);
rc = pt_poll_for_fw_exit_boot_mode(cd, 1500, &wait_time);
if (load_status_str) {
if (!rc)
strcpy(status_str,
"Already in APP mode");
else
strcpy(status_str,
"Already in APP mode - FW stuck in Boot mode");
}
} else if (rc || mode == PT_MODE_UNKNOWN) {
mutex_lock(&cd->system_lock);
cd->mode = mode;
mutex_unlock(&cd->system_lock);
if (load_status_str)
strcpy(status_str, "Failed to determine active mode");
}
exit:
if (!rc)
pt_start_wd_timer(cd);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_exit_bl
*
* SUMMARY: Wrapper function for _pt_pip2_exit_bl that guarantees exclusive
* access.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* *status_str - pointer to optional status string buffer
* buf_size - size of status_str buffer
******************************************************************************/
int pt_pip2_exit_bl(struct pt_core_data *cd, u8 *status_str, int buf_size)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n", __func__,
cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_pip2_exit_bl_(cd, status_str, buf_size);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
__func__);
return rc;
}
/*******************************************************************************
* FUNCTION: _fast_startup
*
* SUMMARY: Perform fast startup after resume device by power on/off stratergy.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
******************************************************************************/
static int _fast_startup(struct pt_core_data *cd)
{
int retry = PT_CORE_STARTUP_RETRY_COUNT;
int rc = 0;
u8 mode = PT_MODE_UNKNOWN;
struct pt_hid_desc hid_desc;
int wait_time = 0;
memset(&hid_desc, 0, sizeof(hid_desc));
reset:
if (retry != PT_CORE_STARTUP_RETRY_COUNT)
pt_debug(cd->dev, DL_INFO, "%s: Retry %d\n", __func__,
PT_CORE_STARTUP_RETRY_COUNT - retry);
if (cd->active_dut_generation == DUT_PIP1_ONLY) {
pt_debug(cd->dev, DL_INFO, "%s: PIP1 Enumeration start\n",
__func__);
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting HID descriptor r=%d\n",
__func__, rc);
if (retry--)
goto reset;
goto exit;
}
cd->mode = pt_get_mode(cd, &hid_desc);
if (cd->mode == PT_MODE_BOOTLOADER) {
pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
__func__);
rc = pt_hid_output_bl_launch_app_(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on launch app r=%d\n",
__func__, rc);
if (retry--)
goto reset;
goto exit;
}
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting HID descriptor r=%d\n",
__func__, rc);
if (retry--)
goto reset;
goto exit;
}
cd->mode = pt_get_mode(cd, &hid_desc);
if (cd->mode == PT_MODE_BOOTLOADER) {
if (retry--)
goto reset;
goto exit;
}
}
cd->enum_status |= ENUM_STATUS_GET_DESC;
cd->enum_status |= ENUM_STATUS_FW_OUT_OF_BOOT;
} else {
pt_debug(cd->dev, DL_INFO, "%s: PIP2 Enumeration start\n",
__func__);
if (retry == PT_CORE_STARTUP_RETRY_COUNT) {
/* Wait for any sentinel before first try */
rc = _pt_request_wait_for_enum_state(
cd->dev, 150,
ENUM_STATUS_BL_RESET_SENTINEL |
ENUM_STATUS_FW_RESET_SENTINEL);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: No Sentinel detected rc = %d\n",
__func__, rc);
} else
pt_flush_bus_if_irq_asserted(cd,
PT_FLUSH_BUS_BASED_ON_LEN);
rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Get mode failed, mode unknown\n",
__func__);
mode = PT_MODE_UNKNOWN;
}
cd->mode = mode;
if (cd->mode == PT_MODE_BOOTLOADER) {
pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
__func__);
rc = pt_pip2_exit_bl_(cd, NULL, 0);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s Failed to exit bootloader\n",
__func__);
msleep(50);
rc = -ENODEV;
if (retry--)
goto reset;
goto exit;
} else {
pt_debug(cd->dev, DL_INFO,
"%s: Exit bootloader succesfully\n",
__func__);
}
if (cd->mode != PT_MODE_OPERATIONAL) {
pt_debug(cd->dev, DL_WARN,
"%s: restore mode failure mode = %d\n",
__func__, cd->mode);
if (retry--)
goto reset;
goto exit;
}
}
cd->enum_status |= ENUM_STATUS_GET_DESC;
}
/* FW cannot handle most PIP cmds when it is still in BOOT mode */
rc = _pt_poll_for_fw_exit_boot_mode(cd, 500, &wait_time);
if (!rc) {
cd->enum_status |= ENUM_STATUS_FW_OUT_OF_BOOT;
pt_debug(cd->dev, DL_WARN,
"%s: Exit FW BOOT Mode after %dms\n",
__func__, wait_time);
} else {
pt_debug(cd->dev, DL_WARN,
"%s: FW stuck in BOOT Mode after %dms\n",
__func__, wait_time);
goto exit;
}
if (!cd->sysinfo.ready) {
rc = pt_hid_output_get_sysinfo_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting sysinfo r=%d\n",
__func__, rc);
if (retry--)
goto reset;
goto exit;
}
}
cd->enum_status |= ENUM_STATUS_GET_SYS_INFO;
rc = pt_restore_parameters_(cd);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: failed to restore parameters rc=%d\n",
__func__, rc);
else
cd->enum_status |= ENUM_STATUS_RESTORE_PARM;
exit:
cd->enum_status |= ENUM_STATUS_COMPLETE;
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_poweron_device_
*
* SUMMARY: Power on device, enable IRQ, and then perform a fast startup.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_poweron_device_(struct pt_core_data *cd)
{
struct device *dev = cd->dev;
int rc = 0;
/*
* After power on action, the chip can general FW sentinel. It can
* trigger an enumeration without hid_reset_cmd_state flag. Since the
* _fast_startup() can perform a quick enumeration too, here doesn't
* need another enumeration.
*/
mutex_lock(&cd->system_lock);
cd->enum_status = ENUM_STATUS_START;
cd->hid_reset_cmd_state = 1;
mutex_unlock(&cd->system_lock);
rc = cd->cpdata->power(cd->cpdata, 1, dev, 0);
if (rc < 0) {
pt_debug(dev, DL_ERROR, "%s: HW Power up fails r=%d\n",
__func__, rc);
goto exit;
}
if (!cd->irq_enabled) {
cd->irq_enabled = true;
enable_irq(cd->irq);
}
/* TBD: following function doesn't update startup_status */
rc = _fast_startup(cd);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_wake_device_from_deep_standby_
*
* SUMMARY: Reset device, and then trigger a full enumeration.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_wake_device_from_deep_standby_(struct pt_core_data *cd)
{
int rc;
rc = pt_dut_reset_and_wait(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
__func__, rc);
goto exit;
}
rc = _fast_startup(cd);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_wake_
*
* SUMMARY: Resume the device with a power on or wake from deep sleep based on
* the configuration in the core platform data structure.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_wake_(struct pt_core_data *cd)
{
int rc = 0;
/*
* Do nothing if system already awake or in progress of waking.
* Proceed if sleeping or entering sleep state.
*/
if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING)
goto exit;
mutex_lock(&cd->system_lock);
cd->sleep_state = SS_WAKING;
mutex_unlock(&cd->system_lock);
// Ensure device is awake to send i2c command
pm_stay_awake(cd->dev);
if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) {
if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
rc = pt_core_wake_device_from_easy_wake_(cd);
else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP)
rc = pt_core_poweron_device_(cd);
else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY)
rc = pt_core_wake_device_from_deep_standby_(cd);
else /* Default action to exit DeepSleep */
rc = pt_core_wake_device_from_deep_sleep_(cd);
}
pm_relax(cd->dev);
mutex_lock(&cd->system_lock);
if (rc == 0)
cd->sleep_state = SS_SLEEP_OFF;
else
cd->sleep_state = SS_SLEEP_ON;
mutex_unlock(&cd->system_lock);
pt_start_wd_timer(cd);
exit:
return rc;
}
/*******************************************************************************
* FUNCTION: pt_core_wake
*
* SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer to core data
******************************************************************************/
static int pt_core_wake(struct pt_core_data *cd)
{
int rc;
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
return rc;
}
rc = pt_core_wake_(cd);
if (release_exclusive(cd, cd->dev) < 0)
pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
__func__);
else
pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
__func__);
return rc;
}
static void on_mcu_display_state_change(const struct touch_callback_data *data)
{
struct pt_core_data *core_data = pt_get_core_data(PT_DEFAULT_CORE_ID);
pr_info("[pt_i2c_adapter] %s: received messages from mcu\n", __func__);
if (core_data == NULL) {
pr_err("[pt_i2c_adapter] %s: Core data is NULL\n", __func__);
return;
}
if (unlikely(!data)) {
pr_err("[pt_i2c_adapter] %s: Callback data is NULL\n", __func__);
return;
}
switch (data->display_mode) {
case MCU_DISPLAY_ON: {
mutex_lock(&core_data->md.mt_lock);
core_data->md.touch_state = WAKING_BY_ACTIVE_TOUCH;
mutex_unlock(&core_data->md.mt_lock);
pt_core_wake(core_data);
break;
}
case MCU_DISPLAY_IDLE:
case MCU_DISPLAY_OFF:
case MCU_DISPLAY_SLEEP:
pt_core_sleep(core_data);
break;
default:
pr_err("[pt_i2c_adapter] %s: no action for display_mode:%d\n", __func__,
data->display_mode);
break;
}
}
void pt_release_queued_input_event(struct work_struct *work)
{
struct pt_input_event *event;
struct pt_input_event *next_event;
struct pt_core_data *core_data = pt_get_core_data(PT_DEFAULT_CORE_ID);
mutex_lock(&core_data->md.mt_lock);
if (core_data->md.touch_state == QUEUEING_TOUCH_EVENTS) {
pt_debug(core_data->dev, DL_INFO, "%s: releasing queued input events\n", __func__);
list_for_each_entry_safe(event, next_event, &touch_event_list_head.list, list) {
if (event->type == EV_SYNC_FRAME)
input_mt_sync_frame(core_data->md.input);
else if (event->type == EV_TIMESTAMP)
input_set_timestamp(core_data->md.input, event->timestamp);
else if (event->type == EV_SLOT_STATE)
input_mt_report_slot_state(core_data->md.input, event->code,
event->value);
else
input_event(core_data->md.input, event->type, event->code,
event->value);
list_del(&event->list);
kfree(event);
}
}
core_data->md.touch_state = NORMAL;
mutex_unlock(&core_data->md.mt_lock);
}
/*******************************************************************************
* FUNCTION: pt_get_ic_crc_
*
* SUMMARY: This function retrieves the config block CRC
*
* NOTE: The post condition of calling this function will be that the DUT will
* be in SCANNINING mode if no failures occur
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* ebid - enumerated block ID
******************************************************************************/
static int pt_get_ic_crc_(struct pt_core_data *cd, u8 ebid)
{
struct pt_sysinfo *si = &cd->sysinfo;
int rc = 0;
u8 status;
u16 calculated_crc = 0;
u16 stored_crc = 0;
rc = pt_pip_suspend_scanning_(cd);
if (rc)
goto error;
rc = pt_pip_verify_config_block_crc_(cd, ebid, &status,
&calculated_crc, &stored_crc);
if (rc)
goto exit;
if (status) {
rc = -EINVAL;
goto exit;
}
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID &&
calculated_crc != stored_crc) {
rc = -EINVAL;
goto exit;
}
si->ttconfig.crc = stored_crc;
exit:
pt_pip_resume_scanning_(cd);
error:
pt_debug(cd->dev, DL_INFO,
"%s: CRC: ebid:%d, calc:0x%04X, stored:0x%04X, rc=%d\n",
__func__, ebid, calculated_crc, stored_crc, rc);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_read_gpio
*
* SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and stores the 32 gpio
* bits into the passed in variable
*
* NOTE: PIP2 READ_GPIO command is only supported in bootloader
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *status - pointer to where the command response status is stored
* *gpio - pointer to device attributes structure
******************************************************************************/
static int pt_pip2_read_gpio(struct device *dev, u8 *status, u32 *gpio)
{
int rc = 0;
u16 actual_read_len;
u8 read_buf[12];
u8 tmp_status = 0;
u8 index = PIP2_RESP_STATUS_OFFSET;
memset(read_buf, 0, ARRAY_SIZE(read_buf));
rc = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_READ_GPIO,
NULL, 0, read_buf, &actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Failed to send PIP2 READ_GPIO cmd\n", __func__);
rc = -ECOMM;
} else {
tmp_status = read_buf[index];
}
if (status)
*status = tmp_status;
if (!rc && gpio && (tmp_status == 0)) {
*gpio = ((read_buf[index + 4] << 24) |
(read_buf[index + 3] << 16) |
(read_buf[index + 2] << 8) |
(read_buf[index + 1]));
}
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_get_panel_id_by_gpio
*
* SUMMARY: Wrapper function to call pt_pip2_read_gpio() to get panel ID
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* *pid - pointer to store panel ID
******************************************************************************/
static int _pt_pip2_get_panel_id_by_gpio(struct pt_core_data *cd, u8 *pid)
{
u32 gpio_value = 0;
u8 status = 0;
u8 panel_id = PANEL_ID_NOT_ENABLED;
int rc = 0;
if (!pid)
return -ENOMEM;
rc = pt_pip2_read_gpio(cd->dev, &status, &gpio_value);
if (!rc) {
if (status == 0) {
panel_id = (gpio_value & PT_PANEL_ID_BITMASK) >>
PT_PANEL_ID_SHIFT;
pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X %s=0x%08X\n",
__func__,
"BL mode PID", panel_id, "gpio", gpio_value);
*pid = panel_id;
} else {
pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
__func__,
"BL read gpio failed status", status);
}
} else {
pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
__func__,
"BL read gpio failed status", status);
}
return rc;
}
/*******************************************************************************
* FUNCTION: pt_enum_with_pip1_dut_
*
* SUMMARY: This function does the full enumeration of the PIP1 DUT with TTDL.
* The core data (cd) startup_status will store, as a bitmask, each
* state of the enumeration process. The startup will be attempted
* PT_CORE_STARTUP_RETRY_COUNT times before giving up.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* reset - Flag to reset the PIP1 only DUT before attempting to enumerate
* *status - poionter to store the enum status bitmask flags
******************************************************************************/
static int pt_enum_with_pip1_dut_(struct pt_core_data *cd, bool reset,
u32 *enum_status)
{
int try = 1;
int rc = 0;
bool detected = false;
u8 return_data[8];
u8 pid = PANEL_ID_NOT_ENABLED;
struct pt_hid_desc hid_desc;
memset(&hid_desc, 0, sizeof(hid_desc));
pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n",
__func__, cd->enum_status, reset);
pt_stop_wd_timer(cd);
reset:
if (try > 1) {
pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n",
__func__, try);
/* Clear enum status except BL/FW sentinel status */
cd->enum_status &= (ENUM_STATUS_BL_RESET_SENTINEL |
ENUM_STATUS_FW_RESET_SENTINEL);
}
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
pt_debug(cd->dev, DL_INFO,
"%s: PIP1 Enumeration start\n", __func__);
/* Only reset the DUT after the first try */
if (reset || (try > 1)) {
/*
* Reset hardware only for Legacy parts. Skip for TT/TC
* parts because if the FW image was loaded directly
* to SRAM issueing a reset ill wipe out what was just
* loaded.
*/
rc = pt_dut_reset_and_wait(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on h/w reset r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
/* sleep to allow FW to be launched if available */
msleep(120);
}
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error getting HID Descriptor r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
rc = -EIO;
goto exit;
}
detected = true;
cd->mode = pt_get_mode(cd, &hid_desc);
/*
* Most systems do not use an XY pin as the panel_id and so
* the BL is used to retrieve the panel_id, however for
* systems that do use an XY pin, the panel_id MUST be
* retrieved from the system information when running FW
* (done below) and so this section of code is skipped.
* Entering the BL here is only needed on XY_PIN systems.
*/
if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
if (cd->mode == PT_MODE_OPERATIONAL) {
rc = pt_pip_start_bootloader_(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on start bootloader r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
cd->mode = PT_MODE_BOOTLOADER;
pt_debug(cd->dev, DL_INFO,
"%s: Bootloader mode\n", __func__);
}
rc = pt_hid_output_bl_get_information_(cd, return_data);
if (!rc) {
cd->bl_info.ready = true;
cd->bl_info.chip_id =
get_unaligned_le16(&return_data[2]);
pt_debug(cd->dev, DL_INFO, "%s: chip ID %04X\n",
__func__, cd->bl_info.chip_id);
} else {
pt_debug(cd->dev, DL_ERROR,
"%s: failed to get chip ID, r=%d\n",
__func__, rc);
}
rc = pt_hid_output_bl_get_panel_id_(cd, &pid);
mutex_lock(&cd->system_lock);
if (!rc) {
cd->pid_for_loader = pid;
pt_debug(cd->dev, DL_INFO,
"%s: Panel ID: 0x%02X\n",
__func__, cd->pid_for_loader);
} else {
cd->pid_for_loader = PANEL_ID_NOT_ENABLED;
pt_debug(cd->dev, DL_WARN,
"%s: Read Failed, disable Panel ID: 0x%02X\n",
__func__, cd->pid_for_loader);
}
mutex_unlock(&cd->system_lock);
}
/* Exit BL due to XY_PIN case or any other cause to be in BL */
if (cd->mode == PT_MODE_BOOTLOADER) {
rc = pt_hid_output_bl_launch_app_(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on launch app r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
/* sleep to allow FW to be launched if available */
msleep(120);
/* Ensure the DUT is now in Application mode */
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error getting HID Desc r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
rc = -EIO;
goto exit;
}
cd->mode = pt_get_mode(cd, &hid_desc);
if (cd->mode == PT_MODE_BOOTLOADER) {
pt_debug(cd->dev, DL_WARN,
"%s: Error confiming exit BL\n",
__func__);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
rc = -EINVAL;
goto exit;
}
}
pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__);
cd->mode = PT_MODE_OPERATIONAL;
/*
* PIP1 only DUTs currently use a hard coded HID Desc and there
* is no PIP support to query when FW exits BOOT mode.
*/
*enum_status |= ENUM_STATUS_GET_DESC;
*enum_status |= ENUM_STATUS_FW_OUT_OF_BOOT;
pt_init_pip_report_fields(cd);
*enum_status |= ENUM_STATUS_GET_RPT_DESC;
if (!cd->features.easywake)
cd->easy_wakeup_gesture = PT_CORE_EWG_NONE;
pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__);
rc = pt_hid_output_get_sysinfo_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting sysinfo r=%d\n", __func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
*enum_status |= ENUM_STATUS_GET_SYS_INFO;
pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n",
__func__,
cd->sysinfo.ttdata.pip_ver_major,
cd->sysinfo.ttdata.pip_ver_minor);
rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: DUT Config block CRC failure rc=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
} else {
*enum_status |= ENUM_STATUS_GET_CFG_CRC;
pt_debug(cd->dev, DL_INFO,
"%s: Config CRC Retrieved (0x%04X)\n",
__func__, *enum_status);
}
rc = pt_restore_parameters_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Failed to restore parameters rc=%d\n",
__func__, rc);
} else {
*enum_status |= ENUM_STATUS_RESTORE_PARM;
pt_debug(cd->dev, DL_INFO,
"%s: RAM parameters restored (0x%04X)\n",
__func__, *enum_status);
}
if (cd->panel_id_support & PT_PANEL_ID_BY_MFG_DATA) {
rc = pt_get_pid_from_mfg_data_(cd, &pid);
mutex_lock(&cd->system_lock);
if (!rc) {
cd->pid_for_loader = pid;
pt_debug(cd->dev, DL_INFO, "%s: Panel ID: 0x%02X\n", __func__,
cd->pid_for_loader);
} else {
pt_debug(cd->dev, DL_WARN, "%s: Read Failed, disable Panel ID: 0x%02X\n",
__func__, cd->pid_for_loader);
}
mutex_unlock(&cd->system_lock);
}
call_atten_cb(cd, PT_ATTEN_STARTUP, 0);
cd->watchdog_interval = PT_WATCHDOG_TIMEOUT;
cd->startup_retry_count = 0;
exit:
/* Generate the HW Version of the connected DUT and store in cd */
_legacy_generate_hw_version(cd, cd->hw_version);
pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__,
cd->hw_version);
if (!detected)
rc = -ENODEV;
pt_debug(cd->dev, DL_WARN,
"%s: Completed Enumeration rc=%d On Attempt %d\n",
__func__, rc, try);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_enum_with_pip2_dut_
*
* SUMMARY: This function does the full enumeration of the PIP2 DUT with TTDL.
* The core data (cd) startup_status will store, as a bitmask, each
* state of the enumeration process. The startup will be attempted
* PT_CORE_STARTUP_RETRY_COUNT times before giving up.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* reset - Flag to reset the PIP1 only DUT before attempting to enumerate
* *status - poionter to store the enum status bitmask flags
******************************************************************************/
static int pt_enum_with_pip2_dut_(struct pt_core_data *cd, bool reset,
u32 *enum_status)
{
int try = 1;
int rc = 0;
int wait_time = 0;
bool detected = false;
u8 mode = PT_MODE_UNKNOWN;
u8 protocol_mode = PT_PROTOCOL_MODE_PIP;
u8 pid = PANEL_ID_NOT_ENABLED;
u8 sys_mode = FW_SYS_MODE_UNDEFINED;
struct pt_hid_desc hid_desc;
bool get_hid_desc = false;
memset(&hid_desc, 0, sizeof(hid_desc));
pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n",
__func__, cd->enum_status, reset);
pt_stop_wd_timer(cd);
reset:
if (try > 1) {
pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n",
__func__, try);
/* Clear enum status except BL/FW sentinel status */
cd->enum_status &= (ENUM_STATUS_BL_RESET_SENTINEL |
ENUM_STATUS_FW_RESET_SENTINEL);
}
pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
/* Generation is PIP2 Capable */
pt_debug(cd->dev, DL_INFO,
"%s: PIP2 Enumeration start\n", __func__);
if (cd->protocol_mode != PT_PROTOCOL_MODE_PIP) {
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (rc)
pt_debug(cd->dev, DL_ERROR,
"%s: Get hid descriptor failed\n",
__func__);
else
get_hid_desc = true;
}
rc = pt_pip2_get_status_(cd);
if (!rc || (rc == PIP2_RSP_ERR_INIT_FAILURE)) {
mode = cd->dut_status.mode;
detected = true;
if (cd->dut_status.fw_system_mode ==
FW_SYS_MODE_SECONDARY_IMAGE) {
cd->mode = mode;
pt_debug(cd->dev, DL_WARN,
"%s: Enumeration not supported from secondary image\n",
__func__);
goto exit;
}
} else {
pt_debug(cd->dev, DL_ERROR,
"%s: Get mode failed, mode unknown\n",
__func__);
mode = PT_MODE_UNKNOWN;
}
cd->mode = mode;
switch (cd->mode) {
case PT_MODE_OPERATIONAL:
pt_debug(cd->dev, DL_INFO,
"%s: Operational mode\n", __func__);
protocol_mode = cd->protocol_mode;
if (cd->app_pip_ver_ready == false) {
rc = pt_pip2_get_version_(cd);
if (!rc)
cd->app_pip_ver_ready = true;
else {
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
}
break;
case PT_MODE_BOOTLOADER:
pt_debug(cd->dev, DL_INFO,
"%s: Bootloader mode\n", __func__);
if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid);
mutex_lock(&cd->system_lock);
if (!rc) {
cd->pid_for_loader = pid;
pt_debug(cd->dev, DL_INFO,
"%s: Panel ID: 0x%02X\n",
__func__, cd->pid_for_loader);
} else {
cd->pid_for_loader =
PANEL_ID_NOT_ENABLED;
pt_debug(cd->dev, DL_WARN,
"%s: Read Failed, disable Panel ID: 0x%02X\n",
__func__, cd->pid_for_loader);
}
mutex_unlock(&cd->system_lock);
}
if (cd->bl_pip_ver_ready == false) {
rc = pt_pip2_get_version_(cd);
if (!rc)
cd->bl_pip_ver_ready = true;
else {
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
}
/*
* Launch app command will fail in flashless mode.
* Skip launch app command here to save time for
* enumeration flow.
*/
if (cd->flashless_dut)
goto exit;
/*
* pt_pip2_launch_app() is needed here instead of
* pt_pip2_exit_bl() because exit_bl will cause several
* hw_resets to occur and the auto BL on a flashless
* DUT will fail.
*/
rc = pt_pip2_launch_app(cd->dev,
PT_CORE_CMD_UNPROTECTED);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on launch app r=%d\n",
__func__, rc);
msleep(50);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
/*
* IRQ thread can be delayed if the serial log print is
* enabled. It causes next command to get wrong response
* Here the delay is to ensure pt_parse_input() to be
* finished.
*/
msleep(60);
/* Check and update the mode */
rc = pt_pip2_get_status_(cd);
if (!rc || (rc == PIP2_RSP_ERR_INIT_FAILURE))
mode = cd->dut_status.mode;
else {
pt_debug(cd->dev, DL_ERROR,
"%s: Get mode failed, mode unknown\n",
__func__);
mode = PT_MODE_UNKNOWN;
}
cd->mode = mode;
if (cd->mode == PT_MODE_OPERATIONAL) {
pt_debug(cd->dev, DL_INFO,
"%s: Launched to Operational mode\n",
__func__);
protocol_mode = cd->protocol_mode;
} else if (cd->mode == PT_MODE_BOOTLOADER) {
pt_debug(cd->dev, DL_ERROR,
"%s: Launch failed, Bootloader mode\n",
__func__);
goto exit;
} else if (cd->mode == PT_MODE_UNKNOWN) {
pt_debug(cd->dev, DL_ERROR,
"%s: Launch failed, Unknown mode\n",
__func__);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
if (cd->app_pip_ver_ready == false) {
rc = pt_pip2_get_version_(cd);
if (!rc)
cd->app_pip_ver_ready = true;
else {
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
}
break;
default:
pt_debug(cd->dev, DL_ERROR,
"%s: Unknown mode\n", __func__);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
if (cd->protocol_mode != PT_PROTOCOL_MODE_PIP &&
get_hid_desc == false) {
rc = pt_get_hid_descriptor_(cd, &hid_desc);
if (!rc)
*enum_status |= ENUM_STATUS_GET_DESC;
} else {
*enum_status |= ENUM_STATUS_GET_DESC;
}
pt_init_pip_report_fields(cd);
if (protocol_mode != PT_PROTOCOL_MODE_PIP) {
rc = pt_get_report_descriptor_(cd);
if (rc < 0) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting report descriptor r=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
}
*enum_status |= ENUM_STATUS_GET_RPT_DESC;
if (!cd->features.easywake)
cd->easy_wakeup_gesture = PT_CORE_EWG_NONE;
pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__);
rc = pt_hid_output_get_sysinfo_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Error on getting sysinfo r=%d\n", __func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
goto exit;
}
*enum_status |= ENUM_STATUS_GET_SYS_INFO;
/* FW cannot handle most PIP cmds when it is still in BOOT mode */
rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time);
if (!rc) {
*enum_status |= ENUM_STATUS_FW_OUT_OF_BOOT;
pt_debug(cd->dev, DL_WARN,
"%s: Exit FW BOOT Mode after %dms, (0x%04X)\n",
__func__, wait_time, *enum_status);
} else {
pt_debug(cd->dev, DL_WARN,
"%s: FW stuck in BOOT Mode after %dms\n",
__func__, wait_time);
goto exit;
}
pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n",
__func__,
cd->sysinfo.ttdata.pip_ver_major,
cd->sysinfo.ttdata.pip_ver_minor);
rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: DUT Config block CRC failure rc=%d\n",
__func__, rc);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
} else {
_pt_get_fw_sys_mode(cd, &sys_mode, NULL);
if (sys_mode != FW_SYS_MODE_SCANNING) {
pt_debug(cd->dev, DL_ERROR,
"%s: scan state: %d, retry: %d\n",
__func__, sys_mode, try);
if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
goto reset;
} else {
*enum_status |= ENUM_STATUS_GET_CFG_CRC;
pt_debug(cd->dev, DL_INFO,
"%s: Config CRC Retrieved (0x%04X)\n",
__func__, *enum_status);
}
}
rc = pt_restore_parameters_(cd);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: Failed to restore parameters rc=%d\n",
__func__, rc);
} else {
*enum_status |= ENUM_STATUS_RESTORE_PARM;
pt_debug(cd->dev, DL_INFO,
"%s: RAM parameters restored (0x%04X)\n",
__func__, *enum_status);
}
call_atten_cb(cd, PT_ATTEN_STARTUP, 0);
cd->watchdog_interval = PT_WATCHDOG_TIMEOUT;
cd->startup_retry_count = 0;
exit:
/* Generate the HW Version of the connected DUT and store in cd */
_pip2_generate_hw_version(cd, cd->hw_version);
pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__,
cd->hw_version);
if (!detected)
rc = -ENODEV;
pt_debug(cd->dev, DL_WARN,
"%s: Completed Enumeration rc=%d On Attempt %d\n",
__func__, rc, try);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_enum_with_dut_
*
* SUMMARY: This function does the full enumeration of the PIP1 or PIP2 DUT with
* TTDL according to the DUT generation.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* reset - Flag to reset the DUT before attempting to enumerate
* *status - pointer to store the enum status bitmask flags
******************************************************************************/
static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset,
u32 *enum_status)
{
int rc = 0;
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, NULL, 0, "enter startup");
#endif
pt_stop_wd_timer(cd);
/* Clear enum status except BL/FW sentinel status */
cd->enum_status &= (ENUM_STATUS_BL_RESET_SENTINEL |
ENUM_STATUS_FW_RESET_SENTINEL);
if (cd->active_dut_generation == DUT_PIP1_ONLY)
rc = pt_enum_with_pip1_dut_(cd, reset, enum_status);
else
rc = pt_enum_with_pip2_dut_(cd, reset, enum_status);
pt_start_wd_timer(cd);
#ifdef TTHE_TUNER_SUPPORT
tthe_print(cd, NULL, 0, "exit startup");
#endif
return rc;
}
/*******************************************************************************
* FUNCTION: pt_enum_with_dut
*
* SUMMARY: This is the safe function wrapper for pt_enum_with_dut_() by
* requesting exclusive access around the call.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *cd - pointer the core data structure
* reset - Flag to reset the DUT before attempting to enumerate
* *status - pointer to store the enum status bitmask flags
******************************************************************************/
static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status)
{
int rc = 0;
mutex_lock(&cd->system_lock);
cd->startup_state = STARTUP_RUNNING;
mutex_unlock(&cd->system_lock);
rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
if (rc) {
pt_debug(cd->dev, DL_ERROR,
"%s: fail get exclusive ex=%p own=%p\n",
__func__, cd->exclusive_dev, cd->dev);
goto exit;
}
rc = pt_enum_with_dut_(cd, reset, status);
if (release_exclusive(cd, cd->dev) < 0)
/* Don't return fail code, mode is already changed. */
pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
__func__);
else
pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
__func__);
exit:
mutex_lock(&cd->system_lock);
/* Complete startup state for any tasks waiting for startup done */
cd->startup_state = STARTUP_DONE;
mutex_unlock(&cd->system_lock);
/* Set STATUS_COMPLETE bit to indicate the status is ready to be read */
*status |= ENUM_STATUS_COMPLETE;
/* Wake the waiters for end of startup */
wake_up(&cd->wait_q);
return rc;
}
#ifndef TTDL_KERNEL_SUBMISSION
static int add_sysfs_interfaces(struct device *dev);
static void remove_sysfs_interfaces(struct device *dev);
static void remove_sysfs_and_modules(struct device *dev);
#endif /*!TTDL_KERNEL_SUBMISSION */
static void pt_release_modules(struct pt_core_data *cd);
static void pt_probe_modules(struct pt_core_data *cd);
/*******************************************************************************
* FUNCTION: _pt_ttdl_restart
*
* SUMMARY: Restarts TTDL enumeration with the DUT and re-probes all modules
*
* NOTE: The function DOSE NOT remove sysfs and modulues. Trying to create
* existing sysfs nodes will produce an error.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int _pt_ttdl_restart(struct device *dev)
{
int rc = 0;
struct pt_core_data *cd = dev_get_drvdata(dev);
#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
struct i2c_client *client =
(struct i2c_client *)container_of(dev, struct i2c_client, dev);
#endif
/*
* Make sure the device is awake, pt_mt_release function will
* cause pm sleep function and lead to deadlock.
*/
pm_runtime_get_sync(dev);
/* Use ttdl_restart_lock to avoid reentry */
mutex_lock(&cd->ttdl_restart_lock);
remove_sysfs_and_modules(cd->dev);
#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
pt_debug(dev, DL_ERROR,
"%s I2C functionality not Supported\n", __func__);
rc = -EIO;
goto ttdl_no_error;
}
#endif
if (cd->active_dut_generation == DUT_UNKNOWN) {
rc = _pt_detect_dut_generation(cd->dev,
&cd->enum_status, &cd->active_dut_generation,
&cd->mode, &cd->protocol_mode);
if ((cd->active_dut_generation == DUT_UNKNOWN) || (rc)) {
pt_debug(dev, DL_ERROR,
"%s: Error, Unknown DUT Generation rc=%d\n",
__func__, rc);
}
}
rc = add_sysfs_interfaces(cd->dev);
if (rc < 0)
pt_debug(cd->dev, DL_ERROR,
"%s: Error, failed sysfs nodes rc=%d\n",
__func__, rc);
if (!(cd->enum_status & ENUM_STATUS_BL_RESET_SENTINEL)
&& !cd->dual_mcu_available) {
pt_debug(dev, DL_INFO, "%s: Call pt_enum_with_dut\n", __func__);
rc = pt_enum_with_dut(cd, true, &cd->enum_status);
if (rc < 0)
pt_debug(dev, DL_ERROR,
"%s: Error, Failed to Enumerate\n", __func__);
}
rc = pt_mt_probe(dev);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Error, fail mt probe\n", __func__);
}
rc = pt_pen_probe(dev);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Error, fail pen probe\n", __func__);
}
rc = pt_btn_probe(dev);
if (rc < 0) {
pt_debug(dev, DL_ERROR,
"%s: Error, fail btn probe\n", __func__);
}
pt_probe_modules(cd);
pt_debug(cd->dev, DL_WARN,
"%s: Well Done! TTDL Restart Completed. (0x%04X)\n",
__func__, cd->enum_status);
rc = 0;
#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
ttdl_no_error:
#endif
mutex_unlock(&cd->ttdl_restart_lock);
mutex_lock(&cd->system_lock);
cd->startup_state = STARTUP_DONE;
mutex_unlock(&cd->system_lock);
pm_runtime_put(dev);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_restart_work_function
*
* SUMMARY: This is the wrapper function placed in a work queue to call
* _pt_ttdl_restart()
*
* RETURN: none
*
* PARAMETERS:
* *work - pointer to the work_struct
******************************************************************************/
static void pt_restart_work_function(struct work_struct *work)
{
struct pt_core_data *cd = container_of(work,
struct pt_core_data, ttdl_restart_work);
int rc = 0;
rc = _pt_ttdl_restart(cd->dev);
if (rc < 0)
pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
__func__, rc);
}
/*******************************************************************************
* FUNCTION: pt_enum_work_function
*
* SUMMARY: This is the wrapper function placed in a work queue to call
* pt_enum_with_dut()
*
* RETURN: none
*
* PARAMETERS:
* *work - pointer to the work_struct
******************************************************************************/
static void pt_enum_work_function(struct work_struct *work)
{
struct pt_core_data *cd = container_of(work,
struct pt_core_data, enum_work);
int rc;
rc = pt_enum_with_dut(cd, false, &cd->enum_status);
if (rc < 0)
pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
__func__, rc);
}
#if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE)
#define KERNEL_VER_GT_3_19
#endif
#if defined(CONFIG_PM_RUNTIME) || defined(KERNEL_VER_GT_3_19)
/* CONFIG_PM_RUNTIME option is removed in 3.19.0 */
#if defined(CONFIG_PM_SLEEP)
/*******************************************************************************
* FUNCTION: pt_core_rt_suspend
*
* SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_sleep.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_rt_suspend(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int rc = 0;
if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
return 0;
rc = pt_core_sleep(cd);
if (rc < 0) {
pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__);
return -EAGAIN;
}
return 0;
}
/*******************************************************************************
* FUNCTION: pt_core_rt_resume
*
* SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_wake.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_rt_resume(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
int rc = 0;
if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
return 0;
rc = pt_core_wake(cd);
if (rc < 0) {
pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__);
return -EAGAIN;
}
return 0;
}
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM_RUNTIME || LINUX_VERSION_CODE */
#if defined(CONFIG_PM_SLEEP)
static void pt_irq_enable(struct pt_core_data *cd, bool enable)
{
struct irq_desc *desc;
if (enable) {
if (!cd->irq_enabled) {
enable_irq(cd->irq);
cd->irq_enabled = true;
}
} else {
if (cd->irq_enabled) {
disable_irq(cd->irq);
cd->irq_enabled = false;
}
}
desc = irq_to_desc(cd->irq);
pt_debug(cd->dev, DL_WARN, "%s: enable=%d, desc->depth=%d\n", __func__, enable,
desc->depth);
}
/*******************************************************************************
* FUNCTION: pt_core_suspend_
*
* SUMMARY: Wrapper function with device suspend/resume stratergy to call
* pt_core_sleep. This function may disable IRQ during sleep state.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_suspend_(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_debug(dev, DL_INFO, "%s start\n", __func__);
pt_core_sleep(cd);
if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
return 0;
pt_debug(dev, DL_INFO, "%s end\n", __func__);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_core_suspend
*
* SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being
* woke up or put to sleep based on Kernel power state even when the display
* is off based on the check of TTDL core platform flag.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_suspend(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_irq_enable(cd, false);
if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
return 0;
return pt_core_suspend_(dev);
}
/*******************************************************************************
* FUNCTION: pt_core_resume_
*
* SUMMARY: Wrapper function with device suspend/resume stratergy to call
* pt_core_wake. This function may enable IRQ before wake up.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_resume_(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_debug(dev, DL_INFO, "%s start\n", __func__);
pt_core_wake(cd);
pt_debug(dev, DL_INFO, "%s end\n", __func__);
return 0;
}
/*******************************************************************************
* FUNCTION: pt_core_resume
*
* SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept
* along with kernel power state even the display is off based on the check of
* TTDL core platform flag.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to core device
******************************************************************************/
static int pt_core_resume(struct device *dev)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_irq_enable(cd, true);
if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
return 0;
return pt_core_resume_(dev);
}
#endif
#ifdef NEED_SUSPEND_NOTIFIER
/*******************************************************************************
* FUNCTION: pt_pm_notifier
*
* SUMMARY: This function is registered to notifier chain and will perform
* suspend operation if match event PM_SUSPEND_PREPARE.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *nb - pointer to notifier_block structure
* action - notifier event type
* *data - void pointer
******************************************************************************/
static int pt_pm_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct pt_core_data *cd = container_of(nb,
struct pt_core_data, pm_notifier);
if (action == PM_SUSPEND_PREPARE) {
pt_debug(cd->dev, DL_INFO, "%s: Suspend prepare\n",
__func__);
/*
* If PM runtime is not suspended, either call runtime
* PM suspend callback or wait until it finishes
*/
if (!pm_runtime_suspended(cd->dev))
pm_runtime_suspend(cd->dev);
(void) pt_core_suspend(cd->dev);
}
return NOTIFY_DONE;
}
#endif
const struct dev_pm_ops pt_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pt_core_suspend, pt_core_resume)
SET_RUNTIME_PM_OPS(pt_core_rt_suspend, pt_core_rt_resume,
NULL)
};
EXPORT_SYMBOL_GPL(pt_pm_ops);
/*******************************************************************************
* FUNCTION: _pt_request_pip2_enter_bl
*
* SUMMARY: Force the DUT to enter the BL by resetting the DUT by use of the
* XRES pin or a soft reset.
*
* NOTE: The WD MUST be stopped/restarted by the calling Function. Having
* the WD active could cause this function to fail!
* NOTE: If start_mode is passed in as PT_MODE_IGNORE, this function
* will not try to determine the current mode but will proceed with
* resetting the DUT and entering the BL.
*
* NOTE: The definition of result code:
* PT_ENTER_BL_PASS (0)
* PT_ENTER_BL_ERROR (1)
* PT_ENTER_BL_RESET_FAIL (2)
* PT_ENTER_BL_HID_START_BL_FAIL (3)
* PT_ENTER_BL_CONFIRM_FAIL (4)
* PT_ENTER_BL_GET_FLASH_INFO_FAIL (5)
*
* RETURNS:
* 0 = success
* !0 = failure
*
*
* PARAMETERS:
* *dev - pointer to device structure
* *start_mode - pointer to the mode the DUT was in when this function
* starts
* *result - pointer to store the result when to enter BL
******************************************************************************/
int _pt_request_pip2_enter_bl(struct device *dev, u8 *start_mode, int *result)
{
int rc = 0;
int t;
int tmp_result = PT_ENTER_BL_ERROR;
int flash_info_retry = 2;
u8 mode = PT_MODE_UNKNOWN;
u8 sys_mode = FW_SYS_MODE_UNDEFINED;
u8 read_buf[32];
u16 actual_read_len;
struct pt_core_data *cd = dev_get_drvdata(dev);
u8 saved_flashless_auto_bl_mode = cd->flashless_auto_bl;
if (cd->watchdog_enabled) {
pt_debug(dev, DL_WARN,
"%s: Watchdog must be stopped before entering BL\n",
__func__);
goto exit;
}
cancel_work_sync(&cd->enum_work);
cancel_work_sync(&cd->watchdog_work);
/* Check and correct "mode" if need when protocol mode is HID */
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID) {
rc = _pt_detect_dut_mode(dev, &mode);
if (!rc && cd->mode != mode) {
pt_debug(dev, DL_WARN,
"%s: cd->mode(%d) doesn't match detected(%d)\n",
__func__, cd->mode, mode);
cd->mode = mode;
}
}
/* if undefined assume operational/test to bypass all checks */
if (*start_mode == PT_MODE_IGNORE) {
mode = PT_MODE_OPERATIONAL;
sys_mode = FW_SYS_MODE_TEST;
pt_debug(dev, DL_INFO, "%s: Assume Mode = %d\n",
__func__, mode);
} else if (*start_mode == PT_MODE_UNKNOWN) {
rc = pt_pip2_get_mode_sysmode_(cd, &mode, &sys_mode);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: Get mode failed, mode unknown\n",
__func__);
}
*start_mode = mode;
pt_debug(dev, DL_INFO, "%s: Get Mode = %d\n", __func__, mode);
} else if (*start_mode == PT_MODE_OPERATIONAL) {
/* Assume SCANNIING mode to avoid doing an extra get_mode */
sys_mode = FW_SYS_MODE_SCANNING;
}
_retry:
/* For Flashless DUTs - Supress auto BL on next BL sentinel */
pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - SUPPRESS\n", __func__);
cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
switch (mode) {
case PT_MODE_SECONDARY_IMAGE:
case PT_MODE_UNKNOWN:
/*
* When either the secondary image is running or the mode
* could not be determined due to the DUT potentially
* running corrupted FW or FW that is not responding to
* the mode request, a hard reset is done.
*/
mutex_lock(&cd->system_lock);
cd->enum_status = ENUM_STATUS_START;
pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
mutex_unlock(&cd->system_lock);
rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
if (rc) {
tmp_result = PT_ENTER_BL_RESET_FAIL;
goto exit;
}
break;
case PT_MODE_OPERATIONAL:
if (sys_mode == FW_SYS_MODE_SCANNING) {
pt_debug(dev, DL_INFO, "%s: Suspend Scanning\n",
__func__);
rc = pt_pip_suspend_scanning_(cd);
if (rc) {
/*
* Print to log but don't exit, the FW could be
* running but be hung or fail to respond to
* this request
*/
pt_debug(dev, DL_ERROR,
"%s Suspend Scan Failed\n", __func__);
}
/* sleep to allow the suspend scan to be processed */
usleep_range(1000, 2000);
}
mutex_lock(&cd->system_lock);
cd->enum_status = ENUM_STATUS_START;
pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
mutex_unlock(&cd->system_lock);
/* Reset device to enter the BL */
if (cd->enter_bl_method == PT_ENTER_BL_BY_RESET_PIN) {
rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
if (rc) {
tmp_result = PT_ENTER_BL_RESET_FAIL;
goto exit;
}
} else {
rc = pt_pip_start_bootloader_(cd);
if (rc) {
tmp_result = PT_ENTER_BL_START_BL_CMD_FAIL;
goto exit;
}
}
break;
case PT_MODE_BOOTLOADER:
/* Do nothing as we are already in the BL */
tmp_result = PT_ENTER_BL_PASS;
goto exit;
default:
/* Should NEVER get here */
tmp_result = PT_ENTER_BL_ERROR;
pt_debug(dev, DL_ERROR, "%s: Unknown mode code\n", __func__);
goto exit;
}
if (!cd->flashless_dut &&
(mode == PT_MODE_UNKNOWN ||
mode == PT_MODE_OPERATIONAL ||
mode == PT_MODE_SECONDARY_IMAGE) &&
cd->enter_bl_method == PT_ENTER_BL_BY_RESET_PIN) {
pt_send_host_mode_cmd_(cd);
}
/*
* To avoid the case that next PIP command can be confused by BL/FW
* sentinel's "wakeup" event, chekcing hid_reset_cmd_state which is
* followed by "wakeup event" function can lower the failure rate.
*/
t = wait_event_timeout(cd->wait_q,
((cd->enum_status != ENUM_STATUS_START)
&& (cd->hid_reset_cmd_state == 0)),
msecs_to_jiffies(300));
if (IS_TMO(t)) {
pt_debug(cd->dev, DL_ERROR,
"%s: TMO waiting for BL sentinel\n", __func__);
}
/* Check if device is now in BL mode */
rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
pt_debug(dev, DL_INFO, "%s: Mode=%d, Status=0x%04X\n", __func__, mode,
cd->enum_status);
if (!rc && mode == PT_MODE_BOOTLOADER) {
pt_debug(dev, DL_INFO, "%s In bootloader mode now\n", __func__);
mutex_lock(&cd->system_lock);
cd->pip2_prot_active = true;
cd->mode = PT_MODE_BOOTLOADER;
mutex_unlock(&cd->system_lock);
tmp_result = PT_ENTER_BL_PASS;
} else {
/*
* If the device doesn't enter BL mode as expected and rc is
* tested pass by above function pt_pip2_get_mode_sysmode_(),
* the function should return an error code to indicate this
* failure PT_ENTER_BL_CONFIRM_FAIL.
*/
if (!rc)
rc = -EINVAL;
tmp_result = PT_ENTER_BL_CONFIRM_FAIL;
mutex_lock(&cd->system_lock);
cd->mode = mode;
mutex_unlock(&cd->system_lock);
pt_debug(dev, DL_ERROR,
"%s ERROR: Not in BL as expected\n", __func__);
}
exit:
if (!cd->flashless_dut && (tmp_result == PT_ENTER_BL_PASS)) {
/* Check to get (buffered) flash information */
rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
PIP2_CMD_ID_FLASH_INFO, NULL, 0,
read_buf, &actual_read_len);
if (!rc) {
if (read_buf[PIP2_RESP_BODY_OFFSET] == 0) {
pt_debug(
dev, DL_WARN,
"%s Unavailable Manufacturer ID: 0x%02x\n",
__func__,
read_buf[PIP2_RESP_BODY_OFFSET]);
/*
* If the BL was unable to cache the correct
* values when entering the first time due to
* the Flash part not having been powered up
* long enough, re-enter the BL to trigger the
* BL to re-attempt to cache the values.
*/
if (flash_info_retry-- > 0) {
mode = PT_MODE_UNKNOWN;
pt_debug(dev, DL_WARN,
"%s Assume mode to UNKNOWN to enter BL again, retry=%d\n",
__func__, flash_info_retry);
goto _retry;
} else {
pt_debug(dev, DL_WARN,
"%s Manufacturer ID Unknown\n",
__func__);
tmp_result = PT_ENTER_BL_PASS;
}
}
} else {
tmp_result = PT_ENTER_BL_GET_FLASH_INFO_FAIL;
pt_debug(
dev, DL_ERROR,
"%s: Failed to send PIP2 READ_FLASH_INFO cmd\n",
__func__);
}
}
pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - %s\n", __func__,
saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" :
"SUPPRESS");
cd->flashless_auto_bl = saved_flashless_auto_bl_mode;
if (result)
*result = tmp_result;
return rc;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_open
*
* SUMMARY: Using the BL PIP2 commands open a file and return the file handle
*
* NOTE: The DUT must be in BL mode for this command to work
*
* RETURNS:
* <0 = Error
* >0 = file handle opened
*
* PARAMETERS:
* *dev - pointer to device structure
* file_no - PIP2 file number to open
******************************************************************************/
int _pt_pip2_file_open(struct device *dev, u8 file_no)
{
int rc = 0;
u16 status;
u16 actual_read_len;
u8 file_handle;
u8 data[2];
u8 read_buf[10];
u8 expected_len = pt_pip2_get_cmd_response_len(PIP2_CMD_ID_FILE_OPEN);
pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no);
data[0] = file_no;
rc = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN,
data, 1, read_buf, &actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: FILE_OPEN timeout for file=%d\n",
__func__, file_no);
return -PIP2_RSP_ERR_NOT_OPEN;
}
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (rc || ((status != PIP2_RSP_ERR_NONE) &&
(status != PIP2_RSP_ERR_ALREADY_OPEN))) {
pt_debug(dev, DL_ERROR,
"%s: %s 0x%02X for file=%d\n",
__func__, "FILE_OPEN failure:", status, file_no);
return -status;
} else if (actual_read_len == expected_len) {
/* File_open returned a file handle */
file_handle = read_buf[PIP2_RESP_BODY_OFFSET];
if (file_handle != file_no) {
pt_debug(dev, DL_ERROR,
"%s: %s 0x%02X file=%d returned handle=%d\n",
__func__, "FILE_OPEN failure:",
status, file_no, file_handle);
return -status;
}
} else {
return -status;
}
return file_handle;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_close
*
* SUMMARY: Using the BL PIP2 commands close a file
*
* NOTE: The DUT must be in BL mode for this command to work
*
* RETURNS:
* <0 = Error
* >0 = file handle closed
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - handle to the file to be closed
******************************************************************************/
int _pt_pip2_file_close(struct device *dev, u8 file_handle)
{
int ret = 0;
u16 status;
u16 actual_read_len;
u8 data[2];
u8 read_buf[32];
pt_debug(dev, DL_DEBUG, "%s: CLOSE file %d\n", __func__, file_handle);
data[0] = file_handle;
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_CLOSE,
data, 1, read_buf, &actual_read_len);
if (ret) {
pt_debug(dev, DL_ERROR,
"%s ROM BL FILE_CLOSE timeout for file = %d\n",
__func__, file_handle);
return -ETIME;
}
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (status != 0x00) {
pt_debug(dev, DL_ERROR,
"%s ROM BL FILE_CLOSE failure: 0x%02X for file = %d\n",
__func__, status, file_handle);
return -status;
}
return file_handle;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_erase_by_file_no
*
* SUMMARY: Using the BL PIP2 commands erase a file by file number only.
*
* NOTE: The DUT must be in BL mode for this command to work
* NOTE: Some FLASH parts can time out while erasing one or more sectors,
* one retry is attempted for each sector in a file.
*
* RETURNS:
* <0 = Error
* >0 = file handle closed
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - handle to the file to be erased
* *status - PIP2 erase status code
******************************************************************************/
int _pt_pip2_file_erase_by_file_no(struct device *dev, u8 file_handle,
int *status)
{
int ret = 0;
int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE;
int retry = 1;
u16 actual_read_len;
u8 data[2];
u8 read_buf[10];
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_debug(dev, DL_DEBUG, "%s: ERASE file %d\n", __func__, file_handle);
data[0] = file_handle;
data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE;
*status = PIP2_RSP_ERR_TIMEOUT;
/* Increase waiting time for large file erase */
mutex_lock(&cd->system_lock);
cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_ERASE_TIMEOUT;
mutex_unlock(&cd->system_lock);
while (*status == PIP2_RSP_ERR_TIMEOUT && retry < max_retry) {
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
data, 2, read_buf, &actual_read_len);
if (ret)
break;
*status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (*status == PIP2_RSP_ERR_TIMEOUT) {
#ifdef TTDL_DIAGNOSTICS
cd->file_erase_timeout_count++;
#endif
pt_debug(dev, DL_WARN,
"%s: ERASE timeout %d for file = %d\n",
__func__, retry, file_handle);
}
retry++;
}
mutex_lock(&cd->system_lock);
cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;
mutex_unlock(&cd->system_lock);
if (ret) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_ERASE cmd failure: %d for file = %d\n",
__func__, ret, file_handle);
return -EIO;
}
if (*status != 0x00) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_ERASE failure: 0x%02X for file = %d\n",
__func__, *status, file_handle);
return -EIO;
}
return file_handle;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_erase_by_file_sector
*
* SUMMARY: Using the BL PIP2 commands erase a file sector by sector.
*
* NOTE: The DUT must be in BL mode for this command to work
* NOTE: Some FLASH parts can time out while erasing one or more sectors,
* one retry is attempted for each sector in a file.
*
* RETURNS:
* <0 = Error
* >0 = file handle closed
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - handle to the file to be erased
* file_sector - Number of sectors to erase
* *status - PIP2 erase status code
******************************************************************************/
int _pt_pip2_file_erase_by_file_sector(struct device *dev, u8 file_handle,
u16 file_sector, int *status)
{
int ret = 0, index = 0;
int max_retry = 3;
int retry = 0;
int tmp_status = PIP2_RSP_ERR_NONE;
u16 sector_to_erase = 1;
u16 actual_read_len;
u8 data[6];
u8 read_buf[10];
struct pt_core_data *cd = dev_get_drvdata(dev);
pt_debug(dev, DL_DEBUG, "%s: ERASE file=%d, sector=%d\n", __func__,
file_handle, file_sector);
data[0] = file_handle;
data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE;
/* Set how many sectors to erase*/
data[4] = LOW_BYTE(sector_to_erase);
data[5] = HI_BYTE(sector_to_erase);
/* Increase waiting time for large file erase */
mutex_lock(&cd->system_lock);
cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT;
mutex_unlock(&cd->system_lock);
for (retry = 0; retry < max_retry; retry++) {
for (index = 0; index < file_sector; index++) {
/* Initialize status code */
tmp_status = PIP2_RSP_ERR_NONE;
/* Set which sector starts to erase */
data[2] = LOW_BYTE(index);
data[3] = HI_BYTE(index);
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
data, 6, read_buf, &actual_read_len);
tmp_status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (ret || tmp_status != PIP2_RSP_ERR_NONE)
break;
}
/*
* Only retry if response doesn't arrive or the status code is
* PIP2_RSP_ERR_TIMEOUT.
*/
if ((ret != -ETIME) || (tmp_status != PIP2_RSP_ERR_TIMEOUT))
break;
else {
#ifdef TTDL_DIAGNOSTICS
cd->file_erase_timeout_count++;
#endif
pt_debug(dev, DL_WARN,
"%s: ERASE timeout %d for file=%d, sector=%d\n",
__func__, retry, file_handle, index);
}
}
/*
* When communication error happens, tmp_status just comes from the
* previous correct response and should not pass to caller.
*/
mutex_lock(&cd->system_lock);
cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;
if (!ret)
*status = tmp_status;
mutex_unlock(&cd->system_lock);
if (ret) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_ERASE cmd failure: %d for file=%d, sector=%d\n",
__func__, ret, file_handle, index);
return -EIO;
}
if (*status != 0x00) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_ERASE failure: 0x%02X for file=%d, sector=%d\n",
__func__, *status, file_handle, index);
return -EIO;
}
return file_handle;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_erase
*
* SUMMARY: Wrapper function to call _pt_pip2_file_erase_by_file_sector() and
* _pt_pip2_file_erase_by_file_no() by checking file_sector.
*
* NOTE: If the file_sector is 0, it will erase the entire file.
*
* RETURNS:
* <0 = Error
* >0 = file handle closed
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - handle to the file to be erased
* file_sector - Number of sectors to erase
* *status - PIP2 erase status code
******************************************************************************/
int _pt_pip2_file_erase(struct device *dev, u8 file_handle, u16 file_sector,
int *status)
{
if (file_sector)
return _pt_pip2_file_erase_by_file_sector(dev, file_handle,
file_sector, status);
else
return _pt_pip2_file_erase_by_file_no(dev, file_handle, status);
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_read
*
* SUMMARY: Using the BL PIP2 commands read n bytes from a already opened file
*
* NOTE: The DUT must be in BL mode for this command to work
*
* RETURNS:
* <0 = Error
* >0 = number of bytes read
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - File handle to read from
* num_bytes - number of bytes to read
******************************************************************************/
int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes,
u8 *read_buf)
{
int ret = 0;
u16 status;
u16 actual_read_len;
u8 data[3];
data[0] = file_handle;
data[1] = (num_bytes & 0x00FF);
data[2] = (num_bytes & 0xFF00) >> 8;
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ,
data, 3, read_buf, &actual_read_len);
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (ret || ((status != 0x00) && (status != 0x03))) {
pt_debug(dev, DL_ERROR,
"%s File open failure with error code = %d\n",
__func__, status);
return -1;
}
ret = num_bytes;
return ret;
}
/*******************************************************************************
* FUNCTION: _pt_read_us_file
*
* SUMMARY: Open a user space file and read 'size' bytes into buf. If size = 0
* then read the entire file.
* NOTE: The file size must be less than PT_PIP2_MAX_FILE_SIZE
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *file_path - pointer to the file path
* *buf - pointer to the buffer to store the file contents
* *size - pointer to the size of the file
******************************************************************************/
#if defined(PT_CORE_READ_USER_SPACE_FILE)
int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size)
{
struct file *filp = NULL;
struct inode *inode = NULL;
unsigned int file_len = 0;
unsigned int read_len = 0;
int rc = 0;
if (file_path == NULL || buf == NULL) {
pt_debug(dev, DL_ERROR, "%s: path || buf is NULL.\n", __func__);
return -EINVAL;
}
pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path);
filp = filp_open(file_path, O_RDONLY, 0400);
if (IS_ERR(filp)) {
pt_debug(dev, DL_WARN, "%s: Failed to open %s\n", __func__,
file_path);
rc = -ENOENT;
goto err;
}
if (filp->f_op == NULL) {
pt_debug(dev, DL_ERROR, "%s: File Operation Method Error\n",
__func__);
rc = -EINVAL;
goto exit;
}
inode = filp->f_path.dentry->d_inode;
if (inode == NULL) {
pt_debug(dev, DL_ERROR, "%s: Get inode from filp failed\n",
__func__);
rc = -EINVAL;
goto exit;
}
file_len = i_size_read(inode->i_mapping->host);
if (file_len == 0) {
pt_debug(dev, DL_ERROR, "%s: file size error,file_len = %d\n",
__func__, file_len);
rc = -EINVAL;
goto exit;
}
if (*size == 0)
read_len = file_len;
else
read_len = *size;
if (read_len > PT_PIP2_MAX_FILE_SIZE) {
pt_debug(dev, DL_ERROR, "%s: file size ( %d ) exception.\n",
__func__, read_len);
rc = -EINVAL;
goto exit;
}
filp->private_data = inode->i_private;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
if (kernel_read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
#else
if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
#endif
pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__);
rc = -EINVAL;
goto exit;
}
*size = read_len;
exit:
if (filp_close(filp, NULL) != 0)
pt_debug(dev, DL_ERROR, "%s: file close error.\n", __func__);
err:
return rc;
}
#else
int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size)
{
return -EINVAL;
}
#endif /* PT_CORE_READ_USER_SPACE_FILE */
/*******************************************************************************
* FUNCTION: _pt_request_pip2_bin_hdr
*
* SUMMARY: Read the stored bin file header from Flash or the User Space file
* in the case of a flashless DUT, and parse the contents
*
* RETURNS:
* 0 = Success
* !0 = Error condition
*
* PARAMETERS:
* *dev - pointer to device structure
******************************************************************************/
int _pt_request_pip2_bin_hdr(struct device *dev, struct pt_bin_file_hdr *hdr)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
u8 file_handle;
u8 read_buf[255];
u8 hdr_len = 0;
u8 i;
int bytes_read;
int read_size;
int ret = 0;
int rc = 0;
bool load_hdr_struct = false;
if (cd->flashless_dut) {
read_size = sizeof(read_buf);
rc = _pt_read_us_file(dev, cd->pip2_us_file_path,
read_buf, &read_size);
if (rc) {
ret = rc;
pt_debug(dev, DL_WARN,
"%s Failed to read fw image from US, rc=%d\n",
__func__, rc);
goto exit;
}
load_hdr_struct = true;
hdr_len = read_buf[0];
i = 0;
} else {
if (cd->mode != PT_MODE_BOOTLOADER) {
ret = -EPERM;
goto exit;
}
/* Open the bin file in Flash */
pt_debug(dev, DL_INFO, "%s Open File 1\n", __func__);
file_handle = _pt_pip2_file_open(dev, PIP2_FW_FILE);
if (file_handle != PIP2_FW_FILE) {
ret = -ENOENT;
pt_debug(dev, DL_ERROR,
"%s Failed to open bin file\n", __func__);
goto exit;
}
/* Read the header length from the file */
pt_debug(dev, DL_INFO, "%s Read length of header\n", __func__);
read_size = 1;
bytes_read = _pt_pip2_file_read(dev, file_handle, read_size,
read_buf);
if (bytes_read != read_size) {
ret = -EX_ERR_FREAD;
pt_debug(dev, DL_ERROR,
"%s Failed to bin file header len\n", __func__);
goto exit_close_file;
}
hdr_len = read_buf[PIP2_RESP_BODY_OFFSET];
if (hdr_len == 0xFF) {
ret = -EX_ERR_FLEN;
pt_debug(dev, DL_ERROR,
"%s Bin header len is invalid\n", __func__);
goto exit_close_file;
}
/* Read the rest of the header from the bin file */
pt_debug(dev, DL_INFO, "%s Read bin file header\n", __func__);
memset(read_buf, 0, sizeof(read_buf));
bytes_read = _pt_pip2_file_read(dev, file_handle, hdr_len,
read_buf);
if (bytes_read != hdr_len) {
ret = -EX_ERR_FREAD;
pt_debug(dev, DL_ERROR,
"%s Failed to read bin file\n", __func__);
goto exit_close_file;
}
load_hdr_struct = true;
exit_close_file:
/* Close the file */
if (file_handle != _pt_pip2_file_close(dev, file_handle)) {
ret = -EX_ERR_FCLOSE;
pt_debug(dev, DL_ERROR,
"%s Failed to close bin file\n", __func__);
}
/*
* The length was already read so subtract 1 to make the rest of
* the offsets match the spec
*/
i = PIP2_RESP_BODY_OFFSET - 1;
}
if (load_hdr_struct) {
hdr->length = hdr_len;
hdr->ttpid = (read_buf[i+1] << 8) | read_buf[i+2];
hdr->fw_major = (read_buf[i+3]);
hdr->fw_minor = (read_buf[i+4]);
hdr->fw_crc = (read_buf[i+5] << 24) |
(read_buf[i+6] << 16) |
(read_buf[i+7] << 8) |
(read_buf[i+8]);
hdr->fw_rev_ctrl = (read_buf[i+9] << 24) |
(read_buf[i+10] << 16) |
(read_buf[i+11] << 8) |
(read_buf[i+12]);
hdr->si_rev = (read_buf[i+14] << 8) | (read_buf[i+13]);
hdr->si_id = (read_buf[i+16] << 8) | (read_buf[i+15]);
hdr->config_ver = (read_buf[i+17] << 8) | (read_buf[i+18]);
if (hdr_len >= 22) {
hdr->hex_file_size = (read_buf[i+19] << 24) |
(read_buf[i+20] << 16) |
(read_buf[i+21] << 8) |
(read_buf[i+22]);
} else {
hdr->hex_file_size = 0;
}
}
exit:
return ret;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_get_stats
*
* SUMMARY: Using the BL PIP2 commands get file information from an already
* opened file
*
* NOTE: The DUT must be in BL mode for this command to work
*
* RETURNS:
* !0 = Error
* 0 = Success
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - File handle to read from
* *address - pointer to store address of file
* *file_size _ pointer to store size of file
******************************************************************************/
int _pt_pip2_file_get_stats(struct device *dev, u8 file_handle, u32 *address,
u32 *file_size)
{
int ret = 1;
u16 status;
u16 actual_read_len;
u8 data[2];
u8 read_buf[16];
data[0] = file_handle;
data[1] = PIP2_FILE_IOCTL_CODE_FILE_STATS;
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
data, 2, read_buf, &actual_read_len);
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (ret || (status != 0x00)) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_STATS failure: 0x%02X for file = %d, ret = %d\n",
__func__, status, file_handle, ret);
ret = -EIO;
goto exit;
}
pt_debug(dev, DL_DEBUG,
"%s --- FILE %d Information ---\n", __func__, file_handle);
if (address) {
*address = read_buf[PIP2_RESP_BODY_OFFSET] +
(read_buf[PIP2_RESP_BODY_OFFSET + 1] << 8) +
(read_buf[PIP2_RESP_BODY_OFFSET + 2] << 16) +
(read_buf[PIP2_RESP_BODY_OFFSET + 3] << 24);
pt_debug(dev, DL_DEBUG, "%s Address: 0x%08x\n",
__func__, *address);
}
if (file_size) {
*file_size = read_buf[PIP2_RESP_BODY_OFFSET + 4] +
(read_buf[PIP2_RESP_BODY_OFFSET + 5] << 8) +
(read_buf[PIP2_RESP_BODY_OFFSET + 6] << 16) +
(read_buf[PIP2_RESP_BODY_OFFSET + 7] << 24);
pt_debug(dev, DL_DEBUG, "%s Size : 0x%08x\n",
__func__, *file_size);
}
exit:
return ret;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_seek_offset
*
* SUMMARY: Using the BL PIP2 commands seek read/write offset for an already
* opened file
*
* NOTE: The DUT must be in BL mode for this command to work
* NOTE: File open/erase command can reset the offset
*
* RETURNS:
* !0 = Error
* 0 = Success
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - File handle to read from
* read_offset - read offset of file
* write_offset - write offset of file
******************************************************************************/
int _pt_pip2_file_seek_offset(struct device *dev, u8 file_handle,
u32 read_offset, u32 write_offset)
{
int ret = 1;
u16 status;
u16 actual_read_len;
u8 data[10];
u8 read_buf[16];
data[0] = file_handle;
data[1] = PIP2_FILE_IOCTL_CODE_SEEK_POINTER;
data[2] = 0xff & read_offset;
data[3] = 0xff & (read_offset >> 8);
data[4] = 0xff & (read_offset >> 16);
data[5] = 0xff & (read_offset >> 24);
data[6] = 0xff & write_offset;
data[7] = 0xff & (write_offset >> 8);
data[8] = 0xff & (write_offset >> 16);
data[9] = 0xff & (write_offset >> 24);
ret = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
data, 10, read_buf, &actual_read_len);
status = read_buf[PIP2_RESP_STATUS_OFFSET];
if (ret || (status != 0x00)) {
pt_debug(dev, DL_ERROR,
"%s ROM FILE_SEEK failure: 0x%02X for file = %d, ret = %d\n",
__func__, status, ret, file_handle);
ret = -EIO;
}
return ret;
}
/*******************************************************************************
* FUNCTION: _pt_pip2_file_crc
*
* SUMMARY: Using the BL PIP2 commands to calculate CRC for a file or portion of
* the file.
*
* NOTE: The DUT must be in BL mode for this command to work
* NOTE: This command only can be used for BL version 1.8 or greater.
* BL version 1.8 added this change according to PGV-173.
*
* RETURNS:
* !0 = Error
* 0 = Success
*
* PARAMETERS:
* *dev - pointer to device structure
* file_handle - File handle to read from
* offset - start offset for CRC calculation
* length - number of bytes to calculate CRC over
* read_buf - pointer to the read buffer
******************************************************************************/
int _pt_pip2_file_crc(struct device *dev, u8 file_handle,
u32 offset, u32 length, u8 *read_buf)
{
int rc = 1;
u16 actual_read_len;
u8 data[10];
data[0] = file_handle;
data[1] = PIP2_FILE_IOCTL_CODE_FILE_CRC;
data[2] = 0xff & offset;
data[3] = 0xff & (offset >> 8);
data[4] = 0xff & (offset >> 16);
data[5] = 0xff & (offset >> 24);
data[6] = 0xff & length;
data[7] = 0xff & (length >> 8);
data[8] = 0xff & (length >> 16);
data[9] = 0xff & (length >> 24);
rc = _pt_request_pip2_send_cmd(dev,
PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
data, 10, read_buf, &actual_read_len);
if (rc)
pt_debug(dev, DL_ERROR,
"%s Return FILE_CRC failure, rc = %d\n",
__func__, rc);
return rc;
}
/*******************************************************************************
* FUNCTION: pt_pip2_ping_test
*
* SUMMARY: BIST type test that uses the PIP2 PING command and ramps up the
* optional payload from 0 bytes to max_bytes and ensures the PIP2
* response payload matches what was sent.
* The max payload size is 247:
* (255 - 2 byte reg address - 4 byte header - 2 byte CRC)
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *dev - pointer to device structure
* *attr - pointer to device attributes
* *buf - pointer to output buffer
******************************************************************************/
int pt_pip2_ping_test(struct device *dev, int max_bytes, int *last_packet_size)
{
struct pt_core_data *cd = dev_get_drvdata(dev);
u16 actual_read_len;
u8 *read_buf = NULL;
u8 *data = NULL;
int data_offset = PIP2_RESP_STATUS_OFFSET;
int i = 1;
int j = 0;
int rc = 0;
read_buf = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
if (!read_buf)
goto ping_test_exit;
data = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
if (!data)
goto ping_test_exit;
/* Load data payload with an array of walking 1's */
for (i = 0; i < 255; i++)
data[i] = 0x01 << (i % 8);
/*
* In HID/PIP3 mode, there is a new "Status" field for "Ping"
* command. So the offset of "Data" should be incremented by 1.
*/
if (cd->protocol_mode == PT_PROTOCOL_MODE_HID)
data_offset = PIP2_RESP_STATUS_OFFSET + 1;
/* Send 'max_bytes' PING cmds using 'i' bytes as payload for each */
for (i = 0; i <= max_bytes; i++) {
rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
PIP2_CMD_ID_PING, data, i, read_buf,
&actual_read_len);
if (rc) {
pt_debug(dev, DL_ERROR,
"%s: PING failed with %d byte payload\n",
__func__, i);
break;
}
/* Verify data returned matches data sent */
for (j = 0; j < i; j++) {
if (read_buf[data_offset + j] != data[j]) {
pt_debug(dev, DL_DEBUG,
"%s: PING packet size %d: sent[%d]=0x%02X recv[%d]=0x%02X\n",
__func__, i, j, data[j], j,
read_buf[data_offset + j]);
goto ping_test_exit;
}
}
}
ping_test_exit:
*last_packet_size = i - 1;
kfree(read_buf);
kfree(data);
return rc;
}
#ifndef TTDL_KERNEL_SUBMISSION
/*******************************************************************************
* FUNCTION: _pt_ic_parse_input_hex
*
* SUMMARY: Parse a char data array as space delimited hex values into
* an int array.
*
* NOTE: _pt_ic_parse_input() function may have simliar work while the type of
* buffer to store data is "u32". This function is still needed by the
* "command" sysfs node because the buffer type to store data is "u8".
*
* RETURN: Length of parsed data
*
* PARAMETERS:
* *dev - pointer to device structure
* *buf - pointer to buffer that holds the input array to parse
* buf_size - size of buf
* *ic_buf - pointer to array to store parsed data
* ic_buf_size - max size of ic_buf
******************************************************************************/
static int _pt_ic_parse_input_hex(struct device *dev, const char *buf,
size_t buf_size, u8 *ic_buf, size_t ic_buf_size)
{
const char *pbuf = buf;
unsigned long value;
char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
u32 i = 0;
u32 j;
int last = 0;
int ret;
pt_debug(dev, DL_DEBUG,
"%s: pbuf=%p buf=%p size=%zu %s=%d buf=%s\n",
__func__, pbuf, buf, buf_size, "scan buf size",
PT_MAX_PIP2_MSG_SIZE, buf);
while (pbuf <= (buf + buf_size)) {
if (i >= PT_MAX_PIP2_MSG_SIZE) {
pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
__func__, "Max cmd size exceeded", i,
PT_MAX_PIP2_MSG_SIZE);
return -EINVAL;
}
if (i >= ic_buf_size) {
pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
__func__, "Buffer size exceeded", i,
ic_buf_size);
return -EINVAL;
}
while (((*pbuf == ' ') || (*pbuf == ','))
&& (pbuf < (buf + buf_size))) {
last = *pbuf;
pbuf++;
}
if (pbuf >= (buf + buf_size))
break;
memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
if ((last == ',') && (*pbuf == ',')) {
pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
__func__, "Invalid data format.");
return -EINVAL;
}
for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
&& (pbuf < (buf + buf_size))
&& (*pbuf != ' ')
&& (*pbuf != ','); j++) {
last = *pbuf;
scan_buf[j] = *pbuf++;
}
ret = kstrtoul(scan_buf, 16, &value);
if (ret < 0) {
pt_debug(dev, DL_ERROR,
"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
"Invalid data format. ", scan_buf,
"Use \"0xHH,...,0xHH\"", " instead.",
i, ret);
return ret;
}
ic_buf[i] = value;
pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lX\n",
__func__, i, value);
i++;
}
return i;
}
/*******************************************************************************
* FUNCTION: _pt_ic_parse_input
*
* SUMMARY: Parse user sysfs input data as a space or comma delimited list of
* hex values or dec values into an int array with the following rules:
* 1) Hex values must have a "0x" prefix for each element or the first element
* only
* 2) Dec values do not have any prefix
* 3) It is not allowed to have a mix of dec and hex values in the user input
* string
*
* RETURN: Number of values parsed
*
* PARAMETERS:
* *dev - pointer to device structure
* *buf - pointer to buffer that holds the input array to parse
* buf_size - size of buf
* *out_buf - pointer to array to store parsed data
* out_buf_size - max size of buffer to be stored
******************************************************************************/
static int _pt_ic_parse_input(struct device *dev,
const char *buf, size_t buf_size,
u32 *out_buf, size_t out_buf_size)
{
const char *pbuf = buf;
unsigned long value;
char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
u32 i = 0;
u32 j;
int last = 0;
int ret = 0;
u8 str_base = 0;
pt_debug(dev, DL_DEBUG,
"%s: in_buf_size=%zu out_buf_size=%zu %s=%d buf=%s\n",
__func__, buf_size, out_buf_size, "scan buf size",
PT_MAX_PIP2_MSG_SIZE, buf);
while (pbuf <= (buf + buf_size)) {
if (i >= PT_MAX_PIP2_MSG_SIZE) {
pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
__func__, "Max cmd size exceeded", i,
PT_MAX_PIP2_MSG_SIZE);
ret = -EINVAL;
goto error;
}
if (i >= out_buf_size) {
pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
__func__, "Buffer size exceeded", i,
out_buf_size);
ret = -EINVAL;
goto error;
}
while (((*pbuf == ' ') || (*pbuf == ','))
&& (pbuf < (buf + buf_size))) {
last = *pbuf;
pbuf++;
}
if (pbuf >= (buf + buf_size))
break;
memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
if ((last == ',') && (*pbuf == ',')) {
pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
__func__, "Invalid data format.");
ret = -EINVAL;
goto error;
}
for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
&& (pbuf < (buf + buf_size))
&& (*pbuf != ' ')
&& (*pbuf != ','); j++) {
last = *pbuf;
scan_buf[j] = *pbuf++;
}
if (i == 0) {
if ((strncmp(scan_buf, "0x", 2) == 0) ||
(strncmp(scan_buf, "0X", 2) == 0))
str_base = 16;
else
str_base = 10;
} else {
if (((strncmp(scan_buf, "0x", 2) == 0) ||
(strncmp(scan_buf, "0X", 2) == 0)) &&
(str_base == 10)) {
pt_debug(dev, DL_ERROR,
"%s: Decimal and Heximal data mixed\n",
__func__);
ret = -EINVAL;
goto error;
}
}
ret = kstrtoul(scan_buf, str_base, &value);
if (ret < 0) {
pt_debug(dev, DL_ERROR,
"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
"Invalid data format. ", scan_buf,
"Use \"0xHH,...,0xHH\" or \"DD DD DD ... DD\"",
" instead.", i, ret);
goto error;
}
out_buf[i] = value;
pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx(%lu)\n",
__func__, i, value, value);
i++;
}
ret = i;
error:
return ret;
}
#endif /* !TTDL_KERNEL_SUBMISSION */
#ifdef TTHE_TUNER_SUPPORT
/*******************************************************************************
* FUNCTION: tthe_debugfs_open
*
* SUMMARY: Open method for tthe_tuner debugfs node. On some hosts the size of
* PT_MAX_PRBUF_SIZE (equal to PAGE_SIZE) is not large enough to handle
* printing a large number of fingers and sensor data without overflowing
* the buffer. tthe_tuner needs ~4K and so the buffer is sized to some
* even multiple of PAGE_SIZE
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *inode - file inode number
* *filp - file pointer to debugfs file
******************************************************************************/
static int tthe_debugfs_open(struct inode *inode, struct file *filp)
{
struct pt_core_data *cd = inode->i_private;
u32 buf_size = PT_MAX_PRBUF_SIZE;
filp->private_data = inode->i_private;
if (cd->tthe_buf)
return -EBUSY;
while (buf_size < 4096)
buf_size = buf_size << 1;
pt_debug(cd->dev, DL_INFO, "%s:PT_MAX_BRBUF_SIZE=%d buf_size=%d\n",
__func__, (int)PT_MAX_PRBUF_SIZE, (int)buf_size);
cd->tthe_buf_size = buf_size;
cd->tthe_buf = kzalloc(cd->tthe_buf_size, GFP_KERNEL);
if (!cd->tthe_buf)
return -ENOMEM;
return 0;
}
/*******************************************************************************
* FUNCTION: tthe_debugfs_close
*
* SUMMARY: Close method for tthe_tuner debugfs node.
*
* RETURN:
* 0 = success
* !0 = failure
*
* PARAMETERS:
* *inode - file inode number
* *filp - file pointer to debugfs file
******************************************************************************/
static int tthe_debugfs_close(struct inode *inode, struct file *filp)
{
struct pt_core_data *cd = filp->private_data;
filp->private_data = NULL;
kfree(cd->tthe_buf);
cd->tthe_buf = NULL;
return 0;
}
/*******************************************************************************
* FUNCTION: tthe_debugfs_store
*
* SUMMARY: Write method for tthe_tuner debugfs node. This function allows
* user configuration of some output values via a bitmask.
* 0x01 = HID USB vs I2C output
* 0xFE = Reserved
*
* RETURN: Size of debugfs data write
*
* PARAMETERS:
* *filp - file pointer to debugfs file
* *buf - the user space buffer to read to
* count - the maximum number of bytes to read
* *ppos - the current position in the buffer
******************************************************************************/
static ssize_t tthe_debugfs_store(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct pt_core_data *cd = filp->private_data;
ssize_t length;
u32 input_data[2];
u8 tmp_buf[4]; /* large enough for 1 32bit value */
int rc = 0;
mutex_lock(&cd->tthe_lock);
/* copy data from user space */
rc = simple_write_to_buffer(tmp_buf, sizeof(tmp_buf),
ppos, buf, count);
if (rc < 0)
goto exit;
length = _pt_ic_parse_input(cd->dev, tmp_buf, count,
input_data, ARRAY_SIZE(input_data));
if (length == 1) {
mutex_lock(&(cd->system_lock));
cd->tthe_hid_usb_format = input_data[0];
if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_USB)
pt_debug(cd->dev, DL_INFO,
"%s: Enable tthe_tuner HID-USB format\n",
__func__);
else if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_I2C)
pt_debug(cd->dev, DL_INFO,
"%s: Enable tthe_tuner HID-I2C format\n",
__func__);
else if (input_data[0] ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP)
pt_debug(cd->dev, DL_INFO,
"%s: Enable tthe_tuner HID-FINGER-TO-PIP format\n",
__func__);
else if (input_data[0] ==
PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP)
pt_debug(cd->dev, DL_INFO,
"%s: Enable tthe_tuner HID_FINGER_AND_PEN_TO_PIP format\n",
__func__);
else {
rc = -EINVAL;
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid parameter: %d\n",
__func__, input_data[0]);
}
mutex_unlock(&(cd->system_lock));
} else {
rc = -EINVAL;
pt_debug(cd->dev, DL_ERROR,
"%s: Invalid number of parameters\n", __func__);
}
exit:
mutex_unlock(&cd->tthe_lock);
if (rc)
return rc;
else
return count;
}
/*******************************************************************************
* FUNCTION: tthe_debugfs_read
*
* SUMMARY: Read method for tthe_tuner debugfs node. This function prints
* tthe_buf to user buffer.
*
* RETURN: Size of debugfs data print
*
* PARAMETERS:
* *filp - file pointer to debugfs file
* *buf - the user space buffer to read to
* count - the maximum number of bytes to read
* *ppos - the current position in the buffer
******************************************************************************/
static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct pt_core_data *cd = filp->private_data;
int size;
int ret;
static int partial_read;
wait_event_interruptible(cd->wait_q,
cd->tthe_buf_len != 0 || cd->tthe_exit);
mutex_lock(&cd->tthe_lock);
if (cd->tthe_exit) {
mutex_unlock(&cd->tthe_lock);
return 0;
}
if (count > cd->tthe_buf_len)
size = cd->tthe_buf_len;
else
size = count;
if (!size) {
mutex_unlock(&cd->tthe_lock);
return 0;
}
if (partial_read) {
ret = copy_to_user(buf, cd->tthe_buf + partial_read, size);
partial_read = 0;
} else {
ret = copy_to_user(buf, cd->tthe_buf, size);
}
/*
* When size >= tthe_buf_len setting partial_read will cause NULL
* characters to be printed in the output.
*/
if (size == count && size < cd->tthe_buf_len)
partial_read = count;
if (ret == size) {
mutex_unlock(&cd->tthe_lock);
return -EFAULT;
}
size -= ret;
cd->tthe_buf_len -= size;
mutex_unlock(&cd->tthe_lock);
*ppos += size;
return size;
}
static const struct file_operations tthe_debugfs_fops = {
.open = tthe_debugfs_open,