i2c: refine the driver of i2c virtualization

1. there's no need to allocate buffer for virtio_i2c_req every
   time when it comes to data transmission, we can prepare this
   buffer at probe stage.

2. we need to add some input parameter checking for avoiding
   security risks.

3. refine the code to reduce the number of judgement statements.

Change-Id: Idb6ae8bb632c1a3032c6ba04de6ebb460f1e1a5c
Signed-off-by: Xianbin Zhu <xianzhu@codeaurora.org>
This commit is contained in:
Xianbin Zhu 2019-06-12 18:50:00 +08:00 committed by Gerrit - the friendly Code Review server
parent cc47c2c325
commit be5afd9343

View file

@ -34,19 +34,6 @@
#define I2C_VIRTIO_WR 0x02 #define I2C_VIRTIO_WR 0x02
#define I2C_VIRTIO_RDWR 0x03 #define I2C_VIRTIO_RDWR 0x03
/**
* struct virtio_i2c - virtio i2c device
* @adapter: i2c adapter
* @vdev: the virtio device
* @vq: i2c virtqueue
*/
struct virtio_i2c {
struct i2c_adapter adapter;
struct virtio_device *vdev;
struct virtqueue *vq;
wait_queue_head_t inq;
};
struct i2c_transfer_head { struct i2c_transfer_head {
u32 type; /* read or write from or to slave */ u32 type; /* read or write from or to slave */
u32 addr; /* slave addr */ u32 addr; /* slave addr */
@ -64,6 +51,21 @@ struct virtio_i2c_req {
struct i2c_transfer_end end; struct i2c_transfer_end end;
}; };
/**
* struct virtio_i2c - virtio i2c device
* @adapter: i2c adapter
* @vdev: the virtio device
* @i2c_req: description of the format of transfer data
* @vq: i2c virtqueue
*/
struct virtio_i2c {
struct i2c_adapter adapter;
struct virtio_device *vdev;
struct virtio_i2c_req i2c_req;
struct virtqueue *vq;
wait_queue_head_t inq;
};
static int virti2c_transfer(struct virtio_i2c *vi2c, static int virti2c_transfer(struct virtio_i2c *vi2c,
struct virtio_i2c_req *i2c_req) struct virtio_i2c_req *i2c_req)
{ {
@ -117,49 +119,35 @@ req_exit:
} }
/* prepare the transfer req */ /* prepare the transfer req */
static struct virtio_i2c_req *virti2c_transfer_prepare(struct i2c_msg *msg_1, static int virti2c_transfer_prepare(struct i2c_msg *msg_1,
struct i2c_msg *msg_2) struct i2c_msg *msg_2, struct virtio_i2c_req *i2c_req)
{ {
char *ptr = NULL; if (IS_ERR_OR_NULL(msg_1) || !msg_1->len ||
int merge = 0; IS_ERR_OR_NULL(msg_1->buf))
struct virtio_i2c_req *i2c_req; return -EINVAL;
if (msg_1 == NULL)
return NULL;
if (msg_2)
merge = 1;
i2c_req = kzalloc(sizeof(struct virtio_i2c_req), GFP_KERNEL);
if (i2c_req == NULL)
return NULL;
if (merge)
ptr = kzalloc((msg_1->len + msg_2->len), GFP_KERNEL);
else
ptr = msg_1->buf;
if (ptr == NULL)
goto err_mem;
/* prepare the head */
i2c_req->head.type = merge ?
I2C_VIRTIO_RDWR : ((msg_1->flags & I2C_M_RD) ?
I2C_VIRTIO_RD : I2C_VIRTIO_WR);
i2c_req->head.addr = msg_1->addr; i2c_req->head.addr = msg_1->addr;
i2c_req->head.length = msg_1->len; i2c_req->head.length = msg_1->len;
if (merge)
if (IS_ERR_OR_NULL(msg_2)) {
i2c_req->head.type = (msg_1->flags & I2C_M_RD) ?
I2C_VIRTIO_RD : I2C_VIRTIO_WR;
i2c_req->buf = msg_1->buf;
} else {
if (!msg_2->len || IS_ERR_OR_NULL(msg_2->buf))
return -EINVAL;
i2c_req->head.type = I2C_VIRTIO_RDWR;
i2c_req->head.total_length = msg_1->len + msg_2->len; i2c_req->head.total_length = msg_1->len + msg_2->len;
/* prepare the buf */ i2c_req->buf = kzalloc((msg_1->len + msg_2->len), GFP_KERNEL);
if (merge) if (IS_ERR_OR_NULL(i2c_req->buf))
memcpy(ptr, msg_1->buf, msg_1->len); return -ENOMEM;
i2c_req->buf = ptr;
return i2c_req; memcpy(i2c_req->buf, msg_1->buf, msg_1->len);
err_mem: }
kfree(i2c_req);
i2c_req = NULL; return 0;
return NULL;
} }
static void virti2c_transfer_end(struct virtio_i2c_req *req, static void virti2c_transfer_end(struct virtio_i2c_req *req,
@ -171,16 +159,15 @@ static void virti2c_transfer_end(struct virtio_i2c_req *req,
req->buf = NULL; req->buf = NULL;
} }
kfree(req); memset(req, 0, sizeof(struct virtio_i2c_req));
req = NULL;
} }
static int virtio_i2c_master_xfer(struct i2c_adapter *adap, static int virtio_i2c_master_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num) struct i2c_msg *msgs, int num)
{ {
int i, ret; int i, ret;
struct virtio_i2c_req *i2c_req;
struct virtio_i2c *vi2c = i2c_get_adapdata(adap); struct virtio_i2c *vi2c = i2c_get_adapdata(adap);
struct virtio_i2c_req *i2c_req = &vi2c->i2c_req;
if (num < 1) { if (num < 1) {
dev_err(&vi2c->vdev->dev, dev_err(&vi2c->vdev->dev,
@ -197,24 +184,22 @@ static int virtio_i2c_master_xfer(struct i2c_adapter *adap,
if (msgs[i].flags & I2C_M_RD) { if (msgs[i].flags & I2C_M_RD) {
/* read the data from slave to master*/ /* read the data from slave to master*/
i2c_req = virti2c_transfer_prepare(&msgs[i], NULL); ret = virti2c_transfer_prepare(&msgs[i], NULL, i2c_req);
} else if ((i + 1 < num) && (msgs[i + 1].flags & I2C_M_RD) && } else if ((i + 1 < num) && (msgs[i + 1].flags & I2C_M_RD) &&
(msgs[i].addr == msgs[i + 1].addr)) { (msgs[i].addr == msgs[i + 1].addr)) {
/* write then read from same address*/ /* write then read from same address*/
i2c_req = virti2c_transfer_prepare(&msgs[i], ret = virti2c_transfer_prepare(&msgs[i],
&msgs[i+1]); &msgs[i+1], i2c_req);
i += 1; i += 1;
} else { } else {
/* write the data to slave */ /* write the data to slave */
i2c_req = virti2c_transfer_prepare(&msgs[i], NULL); ret = virti2c_transfer_prepare(&msgs[i], NULL, i2c_req);
} }
if (i2c_req == NULL) { if (ret)
ret = -ENOMEM;
goto err; goto err;
}
ret = virti2c_transfer(vi2c, i2c_req); ret = virti2c_transfer(vi2c, i2c_req);
virti2c_transfer_end(i2c_req, &msgs[i]); virti2c_transfer_end(i2c_req, &msgs[i]);
if (ret) if (ret)