Re: [PATCH 44/53] Input: atmel_mxt_ts - Verify Information Blockchecksum on probe

From: Yufeng Shen
Date: Fri Jun 07 2013 - 14:37:25 EST


On Wed, Jun 5, 2013 at 1:37 PM, Nick Dyer <nick.dyer@xxxxxxxxxxx> wrote:
> 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 verify that
> we are indeed talking to a chip that supports object protocol correctly. We
> also detect I2C comms problems much earlier, resulting in easier diagnosis.
>
> Signed-off-by: Nick Dyer <nick.dyer@xxxxxxxxxxx>
> Acked-by: Benson Leung <bleung@xxxxxxxxxxxx>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 181 ++++++++++++++++++------------
> 1 file changed, 111 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index bbfea7a..e0ff017 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -194,7 +194,8 @@ struct mxt_data {
> char phys[64]; /* device physical location */
> 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;
> @@ -365,12 +366,16 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
> {
> u8 appmode = data->client->addr;
> u8 bootloader;
> + u8 family_id = 0;
> +
> + if (data->info)
> + family_id = data->info->family_id;
>
> 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;
> }
> @@ -610,7 +615,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;
> @@ -1249,13 +1254,13 @@ static int mxt_check_reg_init(struct mxt_data *data)
> 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");
> ret = -EINVAL;
> goto release;
> }
>
> - 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");
> ret = -EINVAL;
> goto release;
> @@ -1302,7 +1307,7 @@ static int mxt_check_reg_init(struct mxt_data *data)
>
> /* 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);
> @@ -1510,24 +1515,12 @@ static int mxt_acquire_irq(struct mxt_data *data)
> return 0;
> }
>
> -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_object_table(struct mxt_data *data)
> {
> - kfree(data->object_table);
> + kfree(data->raw_info_block);
> data->object_table = NULL;
> + data->info = NULL;
> + data->raw_info_block = NULL;
> kfree(data->msg_buf);
> data->msg_buf = NULL;
> data->enable_reporting = false;
> @@ -1549,25 +1542,17 @@ 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 i2c_client *client = data->client;
> - size_t table_size;
> - int error;
> int i;
> u8 reportid;
> u16 end_address;
>
> - table_size = data->info.object_num * sizeof(struct mxt_object);
> - error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
> - data->object_table);
> - if (error)
> - 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 = data->object_table + i;
> u8 min_id, max_id;
>
> @@ -1591,7 +1576,7 @@ static int mxt_get_object_table(struct mxt_data *data)
>
> switch (object->type) {
> case MXT_GEN_MESSAGE_T5:
> - if (data->info.family_id == 0x80) {
> + if (data->info->family_id == 0x80) {
> /* On mXT224 read and discard unused CRC byte
> * otherwise DMA reads are misaligned */
> data->T5_msg_size = mxt_obj_size(object);
> @@ -1651,22 +1636,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");
> + 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 *buf;
> + struct mxt_info *info;
> + 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);
> + buf = kzalloc(size, GFP_KERNEL);
> + if (!buf) {
> + dev_err(&client->dev, "Failed to allocate memory\n");
> + return -ENOMEM;
> + }
> +
> + error = __mxt_read_reg(client, 0, size, buf);
> + if (error)
> + goto err_free_mem;
> +
> + /* Resize buffer to give space for rest of info block */
> + info = (struct mxt_info *)buf;
> + size += (info->object_num * sizeof(struct mxt_object))
> + + MXT_INFO_CHECKSUM_SIZE;
> +
> + buf = krealloc(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 */
> + 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",
> + data->info_crc, calculated_crc);
> + return -EIO;
> + }
> +
> + /* Save pointers in device data structure */
> + data->raw_info_block = buf;
> + data->info = (struct mxt_info *)buf;
> + data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
> +
> + dev_info(&client->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);
> +

Use data->info->XXX here, info was pointing to buf which could have
changed after krealloc.

> + /* Parse object table information */
> + error = mxt_parse_object_table(data);
> + if (error) {
> + dev_err(&client->dev, "Error %d reading object table\n", error);
> + mxt_free_object_table(data);
> + return error;
> }
>
> return 0;
>
> -free_object_table:
> - mxt_free_object_table(data);
> +err_free_mem:
> + kfree(buf);
> return error;
> }
>
> @@ -1721,13 +1786,12 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
> static int mxt_initialize(struct mxt_data *data)
> {
> struct i2c_client *client = data->client;
> - struct mxt_info *info = &data->info;
> int error;
> bool alt_bootloader_addr = false;
> bool retry = false;
>
> retry_info:
> - error = mxt_get_info(data);
> + error = mxt_read_info_block(data);
> if (error) {
> retry_bootloader:
> error = mxt_probe_bootloader(data, alt_bootloader_addr);
> @@ -1759,21 +1823,6 @@ retry_bootloader:
> }
> }
>
> - data->object_table = kcalloc(info->object_num,
> - sizeof(struct mxt_object),
> - GFP_KERNEL);
> - if (!data->object_table) {
> - dev_err(&client->dev, "Failed to allocate memory\n");
> - return -ENOMEM;
> - }
> -
> - /* Get object table information */
> - error = mxt_get_object_table(data);
> - if (error) {
> - dev_err(&client->dev, "Error %d reading object table\n", error);
> - goto err_free_object_table;
> - }
> -
> error = mxt_acquire_irq(data);
> if (error)
> goto err_free_object_table;
> @@ -1792,17 +1841,6 @@ retry_bootloader:
> goto err_free_object_table;
> }
>
> - error = mxt_read_t9_resolution(data);
> - if (error) {
> - dev_err(&client->dev, "Failed to initialize T9 resolution\n");
> - goto err_free_object_table;
> - }
> -
> - dev_info(&client->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);
> -
> data->enable_reporting = true;
>
> return 0;
> @@ -1817,9 +1855,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 */
> @@ -1827,9 +1865,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,
> @@ -1866,7 +1903,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))
> @@ -2211,6 +2248,10 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
> unsigned int mt_flags = 0;
> int i;
>
> + error = mxt_read_t9_resolution(data);
> + if (error)
> + dev_warn(dev, "Failed to initialize T9 resolution\n");
> +
> input_dev = input_allocate_device();
> if (!input_dev) {
> dev_err(dev, "Failed to allocate memory\n");
> --
> 1.7.10.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/