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