Re: [PATCH v2] Input: atmel_mxt_ts - fix the firmware update

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Wed, Apr 18, 2018 at 06:15:44PM -0300, Ezequiel Garcia wrote:
> From: Nick Dyer <nick@xxxxxxxxxxxxx>
> 
> 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. Fix this issue by retrieving the chip info block
> CRC before the check.
> 
> Note that this solution 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.
> 
> Using this patch on a kevin chromebook, the touchscreen and
> touchpad drivers are able to match the CRC:
> 
>   atmel_mxt_ts 3-004b: Family: 164 Variant: 14 Firmware V2.3.AA Objects: 40
>   atmel_mxt_ts 5-004a: Family: 164 Variant: 17 Firmware V2.0.AA Objects: 31
>   atmel_mxt_ts 3-004b: Resetting device
>   atmel_mxt_ts 5-004a: Resetting device
>   atmel_mxt_ts 3-004b: Config CRC 0x573E89: OK
>   atmel_mxt_ts 3-004b: Touchscreen size X4095Y2729
>   input: Atmel maXTouch Touchscreen as /devices/platform/ff130000.i2c/i2c-3/3-004b/input/input5
>   atmel_mxt_ts 5-004a: Config CRC 0x0AF6BA: OK
>   atmel_mxt_ts 5-004a: Touchscreen size X1920Y1080
>   input: Atmel maXTouch Touchpad as /devices/platform/ff140000.i2c/i2c-5/5-004a/input/input6
> 
> Signed-off-by: Nick Dyer <nick.dyer@xxxxxxxxxxxxx>
> Acked-by: Benson Leung <bleung@xxxxxxxxxxxx>
> [Ezequiel: minor patch massage]
> Signed-off-by: Ezequiel Garcia <ezequiel@xxxxxxxxxxxxx>
> ---

[On GE Healthcare PPD and Motorola Droid 4]
Tested-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxxxx>

-- Sebastian

> Nick, Sebastian:
> 
> After discussing it with Sebastian, I took the liberty
> of re-submitting this, with some massage and a commit log.
> 
> Patch applies to v4.17-rc1. Let me know how it looks.
> 
>  drivers/input/touchscreen/atmel_mxt_ts.c | 186 ++++++++++++++++++-------------
>  1 file changed, 110 insertions(+), 76 deletions(-)
> 
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 5d9699fe1b55..51bfed43f42a 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -280,7 +280,8 @@ struct mxt_data {
>  	struct input_dev *input_dev;
>  	char phys[64];		/* device physical location */
>  	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;
> @@ -460,12 +461,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;
>  		}
> @@ -692,7 +694,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;
> @@ -1462,12 +1464,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;
>  	}
> @@ -1512,7 +1514,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);
> @@ -1563,20 +1565,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) {
> @@ -1591,9 +1579,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;
> @@ -1609,34 +1598,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;
>  
> @@ -1660,8 +1633,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
> @@ -1716,24 +1689,102 @@ 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");
> +	if (!data->msg_buf)
> +		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)
> +		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)
> +		return -ENOMEM;
> +
> +	error = __mxt_read_reg(client, 0, size, id_buf);
> +	if (error)
> +		goto err_free_mem;
> +
> +	/* 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) {
>  		error = -ENOMEM;
> -		goto free_object_table;
> +		goto err_free_mem;
> +	}
> +	id_buf = buf;
> +
> +	/* Read rest of info block */
> +	error = __mxt_read_reg(client, MXT_OBJECT_START,
> +			       size - MXT_OBJECT_START,
> +			       id_buf + MXT_OBJECT_START);
> +	if (error)
> +		goto err_free_mem;
> +
> +	/* Extract & calculate checksum */
> +	crc_ptr = id_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(id_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->raw_info_block = id_buf;
> +	data->info = (struct mxt_info *)id_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, id_buf + MXT_OBJECT_START);
> +	if (error) {
> +		dev_err(&client->dev, "Error %d parsing object table\n", error);
> +		mxt_free_object_table(data);
> +		goto err_free_mem;
>  	}
>  
> -	data->object_table = object_table;
> +	data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START);
>  
>  	return 0;
>  
> -free_object_table:
> -	mxt_free_object_table(data);
> +err_free_mem:
> +	kfree(id_buf);
>  	return error;
>  }
>  
> @@ -2046,7 +2097,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;
>  
> @@ -2077,16 +2128,9 @@ 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;
> +		return error;
>  
>  	error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
>  					&client->dev, GFP_KERNEL, data,
> @@ -2094,14 +2138,10 @@ static int mxt_initialize(struct mxt_data *data)
>  	if (error) {
>  		dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
>  			error);
> -		goto err_free_object_table;
> +		return error;
>  	}
>  
>  	return 0;
> -
> -err_free_object_table:
> -	mxt_free_object_table(data);
> -	return error;
>  }
>  
>  static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
> @@ -2162,7 +2202,7 @@ 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_info *info = data->info;
>  	struct mxt_dbg *dbg = &data->dbg;
>  	unsigned int ofs, page;
>  	unsigned int col = 0;
> @@ -2490,7 +2530,7 @@ 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_info *info = data->info;
>  	struct mxt_dbg *dbg = &data->dbg;
>  	struct mxt_object *object;
>  	int error;
> @@ -2576,7 +2616,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);
> @@ -2601,11 +2640,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;
>  }
>  
> @@ -2614,7 +2648,7 @@ 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;
> +	struct mxt_info *info = data->info;
>  	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
>  			 info->version >> 4, info->version & 0xf, info->build);
>  }
> @@ -2624,7 +2658,7 @@ 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;
> +	struct mxt_info *info = data->info;
>  	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
>  			 info->family_id, info->variant_id);
>  }
> @@ -2663,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.16.3
> 

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux