Re: [PATCHv1] Input: atmel_mxt_ts - fix the firmware update

From: Sebastian Reichel
Date: Wed Apr 04 2018 - 11:05:14 EST


Hi Nick,

On Fri, Mar 23, 2018 at 07:47:36PM +0000, Nick Dyer wrote:
> On Thu, Mar 22, 2018 at 05:43:30PM +0100, Sebastian Reichel wrote:
> > The automatic update mechanism will trigger an update if the
> > info block CRCs are different between maxtouch configuration
> > file (maxtouch.cfg) and chip.
> >
> > The driver compared the CRCs without retrieving the chip CRC,
> > resulting always in a failure and firmware flashing action
> > triggered. The patch will fix this issue by retrieving the
> > chip info block CRC before the check.
>
> Thanks for raising this, I agree it's definitely something we want to
> fix.
>
> However, I'm not convinced you're solving the problem in the best way.
> You've attached it to the read_t9_resolution() code path, whereas the
> info block is common between T9 and T100 and works in the same way.
>
> Would you mind trying the below patch? I've dusted it off from some
> work that I did back in 2012 and it should solve your issue.
>
> It also has the benefit that by reading the information block and the
> object table into a contiguous region of memory, we can verify the
> checksum at probe time. This means we make sure that we are indeed
> talking to a chip that supports object protocol correctly.
>
> Signed-off-by: Nick Dyer <nick.dyer@xxxxxxxxxxxxx>
> Acked-by: Benson Leung <bleung@xxxxxxxxxxxx>

I currently have an unrelated issue that breaks boot on that board,
so I can't test it, but the patch looks mostly good to me. I
noticed, two useless error messages for -ENOMEM. After fixing those
the patch is

Reviewed-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxxxx>

> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 193 +++++++++++++++++++------------
> 1 file changed, 117 insertions(+), 76 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 7659bc48f1db..8a60d91d49a6 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -275,7 +275,8 @@ struct mxt_data {
> char phys[64]; /* device physical location */
> const struct mxt_platform_data *pdata;
> struct mxt_object *object_table;
> - struct mxt_info info;
> + struct mxt_info *info;
> + void *raw_info_block;
> unsigned int irq;
> unsigned int max_x;
> unsigned int max_y;
> @@ -450,12 +451,13 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
> {
> u8 appmode = data->client->addr;
> u8 bootloader;
> + u8 family_id = data->info ? data->info->family_id : 0;
>
> switch (appmode) {
> case 0x4a:
> case 0x4b:
> /* Chips after 1664S use different scheme */
> - if (retry || data->info.family_id >= 0xa2) {
> + if (retry || family_id >= 0xa2) {
> bootloader = appmode - 0x24;
> break;
> }
> @@ -682,7 +684,7 @@ mxt_get_object(struct mxt_data *data, u8 type)
> struct mxt_object *object;
> int i;
>
> - for (i = 0; i < data->info.object_num; i++) {
> + for (i = 0; i < data->info->object_num; i++) {
> object = data->object_table + i;
> if (object->type == type)
> return object;
> @@ -1453,12 +1455,12 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
> data_pos += offset;
> }
>
> - if (cfg_info.family_id != data->info.family_id) {
> + if (cfg_info.family_id != data->info->family_id) {
> dev_err(dev, "Family ID mismatch!\n");
> return -EINVAL;
> }
>
> - if (cfg_info.variant_id != data->info.variant_id) {
> + if (cfg_info.variant_id != data->info->variant_id) {
> dev_err(dev, "Variant ID mismatch!\n");
> return -EINVAL;
> }
> @@ -1503,7 +1505,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
>
> /* Malloc memory to store configuration */
> cfg_start_ofs = MXT_OBJECT_START +
> - data->info.object_num * sizeof(struct mxt_object) +
> + data->info->object_num * sizeof(struct mxt_object) +
> MXT_INFO_CHECKSUM_SIZE;
> config_mem_size = data->mem_size - cfg_start_ofs;
> config_mem = kzalloc(config_mem_size, GFP_KERNEL);
> @@ -1554,20 +1556,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
> return ret;
> }
>
> -static int mxt_get_info(struct mxt_data *data)
> -{
> - struct i2c_client *client = data->client;
> - struct mxt_info *info = &data->info;
> - int error;
> -
> - /* Read 7-byte info block starting at address 0 */
> - error = __mxt_read_reg(client, 0, sizeof(*info), info);
> - if (error)
> - return error;
> -
> - return 0;
> -}
> -
> static void mxt_free_input_device(struct mxt_data *data)
> {
> if (data->input_dev) {
> @@ -1582,9 +1570,10 @@ static void mxt_free_object_table(struct mxt_data *data)
> video_unregister_device(&data->dbg.vdev);
> v4l2_device_unregister(&data->dbg.v4l2);
> #endif
> -
> - kfree(data->object_table);
> data->object_table = NULL;
> + data->info = NULL;
> + kfree(data->raw_info_block);
> + data->raw_info_block = NULL;
> kfree(data->msg_buf);
> data->msg_buf = NULL;
> data->T5_address = 0;
> @@ -1600,34 +1589,18 @@ static void mxt_free_object_table(struct mxt_data *data)
> data->max_reportid = 0;
> }
>
> -static int mxt_get_object_table(struct mxt_data *data)
> +static int mxt_parse_object_table(struct mxt_data *data,
> + struct mxt_object *object_table)
> {
> struct i2c_client *client = data->client;
> - size_t table_size;
> - struct mxt_object *object_table;
> - int error;
> int i;
> u8 reportid;
> u16 end_address;
>
> - table_size = data->info.object_num * sizeof(struct mxt_object);
> - object_table = kzalloc(table_size, GFP_KERNEL);
> - if (!object_table) {
> - dev_err(&data->client->dev, "Failed to allocate memory\n");
> - return -ENOMEM;
> - }
> -
> - error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
> - object_table);
> - if (error) {
> - kfree(object_table);
> - return error;
> - }
> -
> /* Valid Report IDs start counting from 1 */
> reportid = 1;
> data->mem_size = 0;
> - for (i = 0; i < data->info.object_num; i++) {
> + for (i = 0; i < data->info->object_num; i++) {
> struct mxt_object *object = object_table + i;
> u8 min_id, max_id;
>
> @@ -1651,8 +1624,8 @@ static int mxt_get_object_table(struct mxt_data *data)
>
> switch (object->type) {
> case MXT_GEN_MESSAGE_T5:
> - if (data->info.family_id == 0x80 &&
> - data->info.version < 0x20) {
> + if (data->info->family_id == 0x80 &&
> + data->info->version < 0x20) {
> /*
> * On mXT224 firmware versions prior to V2.0
> * read and discard unused CRC byte otherwise
> @@ -1707,24 +1680,108 @@ static int mxt_get_object_table(struct mxt_data *data)
> /* If T44 exists, T5 position has to be directly after */
> if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
> dev_err(&client->dev, "Invalid T44 position\n");
> - error = -EINVAL;
> - goto free_object_table;
> + return -EINVAL;
> }
>
> data->msg_buf = kcalloc(data->max_reportid,
> data->T5_msg_size, GFP_KERNEL);
> if (!data->msg_buf) {
> dev_err(&client->dev, "Failed to allocate message buffer\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +static int mxt_read_info_block(struct mxt_data *data)
> +{
> + struct i2c_client *client = data->client;
> + int error;
> + size_t size;
> + void *id_buf, *buf;
> + uint8_t num_objects;
> + u32 calculated_crc;
> + u8 *crc_ptr;
> +
> + /* If info block already allocated, free it */
> + if (data->raw_info_block != NULL)
> + mxt_free_object_table(data);
> +
> + /* Read 7-byte ID information block starting at address 0 */
> + size = sizeof(struct mxt_info);
> + id_buf = kzalloc(size, GFP_KERNEL);
> + if (!id_buf) {
> + dev_err(&client->dev, "Failed to allocate memory\n");
> + return -ENOMEM;
> + }
> +
> + error = __mxt_read_reg(client, 0, size, id_buf);
> + if (error) {
> + kfree(id_buf);
> + return error;
> + }
> +
> + /* Resize buffer to give space for rest of info block */
> + num_objects = ((struct mxt_info *)id_buf)->object_num;
> + size += (num_objects * sizeof(struct mxt_object))
> + + MXT_INFO_CHECKSUM_SIZE;
> +
> + buf = krealloc(id_buf, size, GFP_KERNEL);
> + if (!buf) {
> + dev_err(&client->dev, "Failed to allocate memory\n");
> error = -ENOMEM;
> - goto free_object_table;
> + goto err_free_mem;
> + }
> +
> + /* Read rest of info block */
> + error = __mxt_read_reg(client, MXT_OBJECT_START,
> + size - MXT_OBJECT_START,
> + buf + MXT_OBJECT_START);
> + if (error)
> + goto err_free_mem;
> +
> + /* Extract & calculate checksum */
> + crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE;
> + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
> +
> + calculated_crc = mxt_calculate_crc(buf, 0,
> + size - MXT_INFO_CHECKSUM_SIZE);
> +
> + /*
> + * CRC mismatch can be caused by data corruption due to I2C comms
> + * issue or else device is not using Object Based Protocol (eg i2c-hid)
> + */
> + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
> + dev_err(&client->dev,
> + "Info Block CRC error calculated=0x%06X read=0x%06X\n",
> + calculated_crc, data->info_crc);
> + error = -EIO;
> + goto err_free_mem;
> }
>
> - data->object_table = object_table;
> + data->raw_info_block = buf;
> + data->info = (struct mxt_info *)buf;
> +
> + dev_info(&client->dev,
> + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
> + data->info->family_id, data->info->variant_id,
> + data->info->version >> 4, data->info->version & 0xf,
> + data->info->build, data->info->object_num);
> +
> + /* Parse object table information */
> + error = mxt_parse_object_table(data, buf + MXT_OBJECT_START);
> + if (error) {
> + dev_err(&client->dev, "Error %d parsing object table\n", error);
> + mxt_free_object_table(data);
> + return error;
> + }
> +
> + data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
>
> return 0;
>
> -free_object_table:
> - mxt_free_object_table(data);
> +err_free_mem:
> + kfree(buf);
> return error;
> }
>
> @@ -2039,7 +2096,7 @@ static int mxt_initialize(struct mxt_data *data)
> int error;
>
> while (1) {
> - error = mxt_get_info(data);
> + error = mxt_read_info_block(data);
> if (!error)
> break;
>
> @@ -2070,13 +2127,6 @@ static int mxt_initialize(struct mxt_data *data)
> msleep(MXT_FW_RESET_TIME);
> }
>
> - /* Get object table information */
> - error = mxt_get_object_table(data);
> - if (error) {
> - dev_err(&client->dev, "Error %d reading object table\n", error);
> - return error;
> - }
> -
> error = mxt_acquire_irq(data);
> if (error)
> goto err_free_object_table;
> @@ -2155,25 +2205,24 @@ static int mxt_init_t7_power_cfg(struct mxt_data *data)
> static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
> unsigned int y)
> {
> - struct mxt_info *info = &data->info;
> struct mxt_dbg *dbg = &data->dbg;
> unsigned int ofs, page;
> unsigned int col = 0;
> unsigned int col_width;
>
> - if (info->family_id == MXT_FAMILY_1386) {
> - col_width = info->matrix_ysize / MXT1386_COLUMNS;
> + if (data->info->family_id == MXT_FAMILY_1386) {
> + col_width = data->info->matrix_ysize / MXT1386_COLUMNS;
> col = y / col_width;
> y = y % col_width;
> } else {
> - col_width = info->matrix_ysize;
> + col_width = data->info->matrix_ysize;
> }
>
> ofs = (y + (x * col_width)) * sizeof(u16);
> page = ofs / MXT_DIAGNOSTIC_SIZE;
> ofs %= MXT_DIAGNOSTIC_SIZE;
>
> - if (info->family_id == MXT_FAMILY_1386)
> + if (data->info->family_id == MXT_FAMILY_1386)
> page += col * MXT1386_PAGES_PER_COLUMN;
>
> return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
> @@ -2483,7 +2532,6 @@ static const struct video_device mxt_video_device = {
>
> static void mxt_debug_init(struct mxt_data *data)
> {
> - struct mxt_info *info = &data->info;
> struct mxt_dbg *dbg = &data->dbg;
> struct mxt_object *object;
> int error;
> @@ -2508,11 +2556,11 @@ static void mxt_debug_init(struct mxt_data *data)
> /* Calculate size of data and allocate buffer */
> dbg->t37_nodes = data->xsize * data->ysize;
>
> - if (info->family_id == MXT_FAMILY_1386)
> + if (data->info->family_id == MXT_FAMILY_1386)
> dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
> else
> dbg->t37_pages = DIV_ROUND_UP(data->xsize *
> - info->matrix_ysize *
> + data->info->matrix_ysize *
> sizeof(u16),
> sizeof(dbg->t37_buf->data));
>
> @@ -2569,7 +2617,6 @@ static int mxt_configure_objects(struct mxt_data *data,
> const struct firmware *cfg)
> {
> struct device *dev = &data->client->dev;
> - struct mxt_info *info = &data->info;
> int error;
>
> error = mxt_init_t7_power_cfg(data);
> @@ -2594,11 +2641,6 @@ static int mxt_configure_objects(struct mxt_data *data,
>
> mxt_debug_init(data);
>
> - dev_info(dev,
> - "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
> - info->family_id, info->variant_id, info->version >> 4,
> - info->version & 0xf, info->build, info->object_num);
> -
> return 0;
> }
>
> @@ -2607,9 +2649,9 @@ static ssize_t mxt_fw_version_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> struct mxt_data *data = dev_get_drvdata(dev);
> - struct mxt_info *info = &data->info;
> return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
> - info->version >> 4, info->version & 0xf, info->build);
> + data->info->version >> 4, data->info->version & 0xf,
> + data->info->build);
> }
>
> /* Hardware Version is returned as FamilyID.VariantID */
> @@ -2617,9 +2659,8 @@ static ssize_t mxt_hw_version_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> struct mxt_data *data = dev_get_drvdata(dev);
> - struct mxt_info *info = &data->info;
> return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
> - info->family_id, info->variant_id);
> + data->info->family_id, data->info->variant_id);
> }
>
> static ssize_t mxt_show_instance(char *buf, int count,
> @@ -2656,7 +2697,7 @@ static ssize_t mxt_object_show(struct device *dev,
> return -ENOMEM;
>
> error = 0;
> - for (i = 0; i < data->info.object_num; i++) {
> + for (i = 0; i < data->info->object_num; i++) {
> object = data->object_table + i;
>
> if (!mxt_object_readable(object->type))
> --
> 2.14.1
>

Attachment: signature.asc
Description: PGP signature