Re: [PATCH] Input: elants_i2c - Add Remark ID check flow in firmware update function

From: 'Dmitry Torokhov'
Date: Tue Dec 03 2019 - 14:48:14 EST


Hi Johnny,

On Tue, Nov 19, 2019 at 01:59:45PM +0800, Johnny.Chuang wrote:
> This patch add Remark ID check flow to firmware update function of elan
> touchscreen driver.
>
> It avoids firmware update with mismatched Remark ID.
>
> This function is supported by our latest version of boot code, but it
> cooperates well with earlier versions.
>
> Our driver will decide if enable Remark ID check with boot code version.
>
> Signed-off-by: Johnny Chuang <johnny.chuang@xxxxxxxxxx>
> ---
> drivers/input/touchscreen/elants_i2c.c | 108
> ++++++++++++++++++++++++++++++---
> 1 file changed, 100 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/input/touchscreen/elants_i2c.c
> b/drivers/input/touchscreen/elants_i2c.c
> index d4ad24e..9a17af6 100644
> --- a/drivers/input/touchscreen/elants_i2c.c
> +++ b/drivers/input/touchscreen/elants_i2c.c
> @@ -59,8 +59,10 @@
> #define CMD_HEADER_WRITE 0x54
> #define CMD_HEADER_READ 0x53
> #define CMD_HEADER_6B_READ 0x5B
> +#define CMD_HEADER_ROM_READ 0x96
> #define CMD_HEADER_RESP 0x52
> #define CMD_HEADER_6B_RESP 0x9B
> +#define CMD_HEADER_ROM_RESP 0x95
> #define CMD_HEADER_HELLO 0x55
> #define CMD_HEADER_REK 0x66
>
> @@ -128,6 +130,7 @@ struct elants_data {
> u8 bc_version;
> u8 iap_version;
> u16 hw_version;
> + u16 remark_id;

We only use this during firmware version check phase, no need to store
it in the device data structure.

> unsigned int x_res; /* resolution in units/mm */
> unsigned int y_res;
> unsigned int x_max;
> @@ -200,6 +203,10 @@ static int elants_i2c_execute_command(struct i2c_client
> *client,
> expected_response = CMD_HEADER_6B_RESP;
> break;
>
> + case CMD_HEADER_ROM_READ:
> + expected_response = CMD_HEADER_ROM_RESP;
> + break;
> +
> default:
> dev_err(&client->dev, "%s: invalid command %*ph\n",
> __func__, (int)cmd_size, cmd);
> @@ -556,6 +563,8 @@ static int elants_i2c_initialize(struct elants_data *ts)
>
> /* hw version is available even if device in recovery state */
> error2 = elants_i2c_query_hw_version(ts);
> + if (!error2)
> + error2 = elants_i2c_query_bc_version(ts);

Can you please explain why this change is done? This does not seem to
relate to the "remark id" functionality. Should it be a separate change?

> if (!error)
> error = error2;
>
> @@ -564,8 +573,6 @@ static int elants_i2c_initialize(struct elants_data *ts)
> if (!error)
> error = elants_i2c_query_test_version(ts);
> if (!error)
> - error = elants_i2c_query_bc_version(ts);
> - if (!error)
> error = elants_i2c_query_ts_info(ts);
>
> if (error)
> @@ -613,39 +620,124 @@ static int elants_i2c_fw_write_page(struct i2c_client
> *client,
> return error;
> }
>
> +static int elants_i2c_query_remark_id(struct elants_data *ts) {
> + struct i2c_client *client = ts->client;
> + int error;
> + const u8 cmd[] = { CMD_HEADER_ROM_READ, 0x80, 0x1F, 0x00, 0x00, 0x21
> };
> + u8 resp[6] = { 0 };
> +
> + error = elants_i2c_execute_command(client, cmd, sizeof(cmd),
> + resp, sizeof(resp));
> + if (error) {
> + dev_err(&client->dev, "get Remark ID failed: %d.\n", error);
> + return error;
> + }
> +
> + ts->remark_id = get_unaligned_be16(&resp[3]);
> + dev_info(&client->dev, "remark_id=0x%04x.\n", ts->remark_id);

I do not think we need be this noisy. Either dev_dbg, or drop it
completely.

> +
> + return 0;
> +}
> +
> +static int elants_i2c_validate_remark_id(struct elants_data *ts,
> + const struct firmware *fw)
> +{
> + struct i2c_client *client = ts->client;
> + int error;
> + u16 fw_remark_id = 0;
> +
> + /* Compare TS Remark ID and FW Remark ID */
> + error = elants_i2c_query_remark_id(ts);
> + if (error) {
> + dev_err(&client->dev, "failed to query Remark ID: %d\n",
> error);
> + return error;
> + }
> +
> + fw_remark_id = get_unaligned_le16(&fw->data[fw->size - 4]);
> + dev_info(&client->dev, "fw_remark_id=0x%04x.\n", fw_remark_id);

Please drop this dev_info().

> + if (fw_remark_id != ts->remark_id) {
> + dev_err(&client->dev,
> + "Remark ID Mismatched: ts_remark_id=0x%04x,
> fw_remark_id=0x%x.\n",

You can use "%#04x" to format with prefix.

> + ts->remark_id, fw_remark_id);
> + return -ENODATA;

I'd say -EINVAL here.

> + }
> +
> + return 0;
> +}
> +
> static int elants_i2c_do_update_firmware(struct i2c_client *client,
> const struct firmware *fw,
> bool force)
> {
> + struct elants_data *ts = i2c_get_clientdata(client);
> + static const u8 w_flashkey[] = { 0x54, 0xC0, 0xE1, 0x5A };
> const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 };
> const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 };
> const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc };
> - const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01};
> + const u8 close_idle[] = { 0x54, 0x2c, 0x01, 0x01 };
> u8 buf[HEADER_SIZE];
> u16 send_id;
> int page, n_fw_pages;
> int error;
> + bool check_remark_id = ts->iap_version >= 0x60;
>
> /* Recovery mode detection! */
> if (force) {
> dev_dbg(&client->dev, "Recovery mode procedure\n");
> +
> + if (check_remark_id == true) {

Simply
if (check_remark_id) {


> + /* Validate Remark ID */

This comment is not needed, you named the function that you are calling
below well and its name describes what we are trying to do perfectly.

> + error = elants_i2c_validate_remark_id(ts, fw);
> + if (error) {
> + dev_err(&client->dev,
> + "failed to validate Remark ID:
> %d\n",
> + error);

elants_i2c_validate_remark_id() already gives necessary diagnostic, this
message is not needed.

> + return error;
> + }
> + }
> +
> + error = elants_i2c_send(client, w_flashkey,
> sizeof(w_flashkey));
> + if (error)
> + dev_err(&client->dev, "failed to write flash key:
> %d\n",
> + error);

Sending flashkey in this chunk seems to be another change not directly
related to the remark id. Why do we need this? Should it be split out?

> +
> error = elants_i2c_send(client, enter_iap2,
> sizeof(enter_iap2));
> + if (error) {
> + dev_err(&client->dev, "failed to enter IAP mode:
> %d\n",
> + error);
> + return error;
> + }
> + msleep(20);

We already have msleep(20) in the common path below, do we really need
2nd one here?

> } else {
> /* Start IAP Procedure */
> dev_dbg(&client->dev, "Normal IAP procedure\n");
> +
> /* Close idle mode */
> error = elants_i2c_send(client, close_idle,
> sizeof(close_idle));
> if (error)
> dev_err(&client->dev, "Failed close idle: %d\n",
> error);
> msleep(60);
> +
> elants_i2c_sw_reset(client);
> msleep(20);
> - error = elants_i2c_send(client, enter_iap,
> sizeof(enter_iap));
> - }
>
> - if (error) {
> - dev_err(&client->dev, "failed to enter IAP mode: %d\n",
> error);
> - return error;
> + if (check_remark_id == true) {

if (check_remark_id) {

> + /* Validate Remark ID */

Drop comment.

> + error = elants_i2c_validate_remark_id(ts, fw);
> + if (error) {
> + dev_err(&client->dev, "failed to validate
> Remark ID: %d\n",
> + error);

Drop message.

> + return error;
> + }
> + }
> +
> + error = elants_i2c_send(client, enter_iap,
> sizeof(enter_iap));
> + if (error) {
> + dev_err(&client->dev, "failed to enter IAP mode:
> %d\n",
> + error);
> + return error;
> + }
> }
>
> msleep(20);
> --
> 2.7.4
>

Thanks.

--
Dmitry