|
@@ -101,6 +101,7 @@ struct at91_twi_dev {
|
|
|
unsigned twi_cwgr_reg;
|
|
|
struct at91_twi_pdata *pdata;
|
|
|
bool use_dma;
|
|
|
+ bool recv_len_abort;
|
|
|
struct at91_twi_dma dma;
|
|
|
};
|
|
|
|
|
@@ -267,12 +268,24 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
|
|
|
*dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff;
|
|
|
--dev->buf_len;
|
|
|
|
|
|
+ /* return if aborting, we only needed to read RHR to clear RXRDY*/
|
|
|
+ if (dev->recv_len_abort)
|
|
|
+ return;
|
|
|
+
|
|
|
/* handle I2C_SMBUS_BLOCK_DATA */
|
|
|
if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) {
|
|
|
- dev->msg->flags &= ~I2C_M_RECV_LEN;
|
|
|
- dev->buf_len += *dev->buf;
|
|
|
- dev->msg->len = dev->buf_len + 1;
|
|
|
- dev_dbg(dev->dev, "received block length %d\n", dev->buf_len);
|
|
|
+ /* ensure length byte is a valid value */
|
|
|
+ if (*dev->buf <= I2C_SMBUS_BLOCK_MAX && *dev->buf > 0) {
|
|
|
+ dev->msg->flags &= ~I2C_M_RECV_LEN;
|
|
|
+ dev->buf_len += *dev->buf;
|
|
|
+ dev->msg->len = dev->buf_len + 1;
|
|
|
+ dev_dbg(dev->dev, "received block length %d\n",
|
|
|
+ dev->buf_len);
|
|
|
+ } else {
|
|
|
+ /* abort and send the stop by reading one more byte */
|
|
|
+ dev->recv_len_abort = true;
|
|
|
+ dev->buf_len = 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* send stop if second but last byte has been read */
|
|
@@ -444,6 +457,12 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
|
|
|
ret = -EIO;
|
|
|
goto error;
|
|
|
}
|
|
|
+ if (dev->recv_len_abort) {
|
|
|
+ dev_err(dev->dev, "invalid smbus block length recvd\n");
|
|
|
+ ret = -EPROTO;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
dev_dbg(dev->dev, "transfer complete\n");
|
|
|
|
|
|
return 0;
|
|
@@ -500,6 +519,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
|
|
|
dev->buf_len = m_start->len;
|
|
|
dev->buf = m_start->buf;
|
|
|
dev->msg = m_start;
|
|
|
+ dev->recv_len_abort = false;
|
|
|
|
|
|
ret = at91_do_twi_transfer(dev);
|
|
|
|