From e388a3537b138d16091ec61c4a7e8edca8024555 Mon Sep 17 00:00:00 2001 From: liuyc20 Date: Thu, 27 Jun 2019 16:05:01 +0800 Subject: [PATCH] vl53l1: fix i2c dma transfer crash If the i2c master starts the dma transfer, the buffer must be physical continous. or i2c_transfer will crash. __dma_inv_area+0x28/0x58 geni_se_rx_dma_prep+0xb0/0x208 geni_i2c_xfer+0x2e8/0xf88 __i2c_transfer+0x598/0x790 i2c_transfer+0xa0/0xe8 cci_read.isra.1+0xa4/0x140 [stmvl53l1] VL53L1_ReadMulti+0x3c/0x54 [stmvl53l1] VL53L1_get_nvm_copy_data+0x40/0x88 [stmvl53l1] VL53L1_read_p2p_data+0xbc/0x210 [stmvl53l1] VL53L1_data_init+0x248/0x254 [stmvl53l1] VL53L1_DataInit+0x28/0xcc [stmvl53l1] stmvl53l1_setup+0x4a8/0x9f0 [stmvl53l1] stmvl53l1_probe+0x1e4/0x8c8 [stmvl53l1] i2c_device_probe+0x258/0x2e0 driver_probe_device+0x4c0/0x658 __driver_attach+0x104/0x1b0 bus_for_each_dev+0x8c/0xd8 driver_attach+0x2c/0x38 bus_add_driver+0x138/0x240 driver_register+0xac/0xf8 i2c_register_driver+0x4c/0xd0 stmvl53l1_init_i2c+0x44/0x188 [stmvl53l1] stmvl53l1_init+0x3c/0x1000 [stmvl53l1] Change-Id: I8c328f1e5187dbd1de80aa700aa560ef29a9ed4d Signed-off-by: liuyc20 Reviewed-on: https://gerrit.mot.com/1377597 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Huosheng Liao Reviewed-by: Dawei Wang Submit-Approved: Jira Key --- drivers/input/misc/vl53L1/stmvl53l1-i2c.h | 6 +++ drivers/input/misc/vl53L1/stmvl53l1_i2c.c | 46 ++++++++++++++++--- .../input/misc/vl53L1/stmvl53l1_module-i2c.c | 6 +++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/vl53L1/stmvl53l1-i2c.h b/drivers/input/misc/vl53L1/stmvl53l1-i2c.h index 726c73e01519..65f2cc05475a 100644 --- a/drivers/input/misc/vl53L1/stmvl53l1-i2c.h +++ b/drivers/input/misc/vl53L1/stmvl53l1-i2c.h @@ -93,6 +93,12 @@ struct i2c_data { struct msgtctrl_t { unsigned unhandled_irq_vec:1; } msg_flag; + + struct i2c_dma_data { + struct mutex lock; + uint8_t *data; + unsigned int len; + } dma_data; }; int stmvl53l1_init_i2c(void); diff --git a/drivers/input/misc/vl53L1/stmvl53l1_i2c.c b/drivers/input/misc/vl53L1/stmvl53l1_i2c.c index 440604f67510..32b41a7e7bdd 100644 --- a/drivers/input/misc/vl53L1/stmvl53l1_i2c.c +++ b/drivers/input/misc/vl53L1/stmvl53l1_i2c.c @@ -111,7 +111,6 @@ static uint32_t tv_elapsed_ms(struct timeval *tv) static int cci_write(struct stmvl53l1_data *dev, int index, uint8_t *data, uint16_t len) { - uint8_t buffer[STMVL53L1_MAX_CCI_XFER_SZ + 2]; struct i2c_msg msg; struct i2c_data *i2c_client_obj = (struct i2c_data *)dev->client_object; struct i2c_client *client = (struct i2c_client *)i2c_client_obj->client; @@ -122,16 +121,30 @@ static int cci_write(struct stmvl53l1_data *dev, int index, vl53l1_errmsg("invalid len %d\n", len); return -1; } + + mutex_lock(&i2c_client_obj->dma_data.lock); + if (i2c_client_obj->dma_data.len < len + 2) { + if (i2c_client_obj->dma_data.data) + kfree(i2c_client_obj->dma_data.data); + i2c_client_obj->dma_data.data = kzalloc(len + 2, GFP_KERNEL); + if (!i2c_client_obj->dma_data.data) { + i2c_client_obj->dma_data.len = 0; + mutex_unlock(&i2c_client_obj->dma_data.lock); + return -1; + } + i2c_client_obj->dma_data.len = len + 2; + } + cci_access_start(); /* build up little endian index in buffer */ - buffer[0] = (index >> 8) & 0xFF; - buffer[1] = (index >> 0) & 0xFF; + i2c_client_obj->dma_data.data[0] = (index >> 8) & 0xFF; + i2c_client_obj->dma_data.data[1] = (index >> 0) & 0xFF; /* copy write data to buffer after index */ - memcpy(buffer + 2, data, len); + memcpy(i2c_client_obj->dma_data.data + 2, data, len); /* set i2c msg */ msg.addr = client->addr; msg.flags = client->flags; - msg.buf = buffer; + msg.buf = i2c_client_obj->dma_data.data; msg.len = len + 2; rc = i2c_transfer(client->adapter, &msg, 1); @@ -139,6 +152,9 @@ static int cci_write(struct stmvl53l1_data *dev, int index, vl53l1_errmsg("wr i2c_transfer err:%d, index 0x%x len %d\n", rc, index, len); } + + mutex_unlock(&i2c_client_obj->dma_data.lock); + cci_access_over("rd status %d long %d ", rc != 1, len); return rc != 1; } @@ -157,6 +173,20 @@ static int cci_read(struct stmvl53l1_data *dev, int index, vl53l1_errmsg("invalid len %d\n", len); return -1; } + + mutex_lock(&i2c_client_obj->dma_data.lock); + if (i2c_client_obj->dma_data.len < len) { + if (i2c_client_obj->dma_data.data) + kfree(i2c_client_obj->dma_data.data); + i2c_client_obj->dma_data.data = kzalloc(len, GFP_KERNEL); + if (!i2c_client_obj->dma_data.data) { + i2c_client_obj->dma_data.len = 0; + mutex_unlock(&i2c_client_obj->dma_data.lock); + return -1; + } + i2c_client_obj->dma_data.len = len; + } + cci_access_start(); /* build up little endian index in buffer */ @@ -170,7 +200,7 @@ static int cci_read(struct stmvl53l1_data *dev, int index, /* read part of the i2c transaction */ msg[1].addr = client->addr; msg[1].flags = I2C_M_RD | client->flags; - msg[1].buf = data; + msg[1].buf = i2c_client_obj->dma_data.data; msg[1].len = len; rc = i2c_transfer(client->adapter, msg, 2); @@ -179,6 +209,10 @@ static int cci_read(struct stmvl53l1_data *dev, int index, __func__, rc, client->addr, index, len); } + + memcpy(data, i2c_client_obj->dma_data.data, len); + mutex_unlock(&i2c_client_obj->dma_data.lock); + cci_access_over(" wr len %d status %d", rc != 2, len); return rc != 2; } diff --git a/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c b/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c index 348189376850..87cd641d3114 100644 --- a/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c +++ b/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c @@ -506,6 +506,8 @@ static int stmvl53l1_probe(struct i2c_client *client, i2c_data->client = client; i2c_data->vl53l1_data = vl53l1_data; i2c_data->irq = -1 ; /* init to no irq */ + i2c_data->dma_data.len = 0; + mutex_init(&i2c_data->dma_data.lock); /* parse and configure hardware */ rc = stmvl53l1_parse_tree(&i2c_data->client->dev, i2c_data); @@ -921,6 +923,10 @@ static void memory_release(struct kref *kref) struct i2c_data *data = container_of(kref, struct i2c_data, ref); vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->dma_data.lock); + if (data->dma_data.len > 0 && data->dma_data.data) + kfree(data->dma_data.data); + mutex_unlock(&data->dma_data.lock); kfree(data->vl53l1_data); kfree(data); vl53l1_dbgmsg("End\n");