arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c: Cancel timeout on i2c stop

Not canceling the I2C timeout watch dog immediately after finishing
all I2C transactions in interrupt context can lead to a race condition
due to nxsem_wait(&priv->wait) in lpc17_40_i2c_start() not resuming
execution fast enough (this can be easily triggered if another task /
thread is using a lot of cpu time).

Falling to cancel the watchdog up to time will cause the priv->wait
semaphore to be incremented twice (first by lpc17_40_i2c_stop() then
by lpc17_40_i2c_timeout()), so all I2C transactions after that will
return immediately and priv->msgs will hold pointers to memory it
doesn't own anymore.

Canceling the priv->timeout watch dog in lpc17_40_i2c_stop() prevents
this as it is executed from the I2C interrupt handler.

arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c: Fix timeout calculation

For each byte received / transmitted, an acknowledge bit is also
transmitted / received, requiring effectively 9 bits for each byte.
This commit is contained in:
Augusto Fraga Giachero 2020-03-02 16:34:54 -03:00 committed by Abdelatif Guettouche
parent 3b53cd1e57
commit 0be87af99d

View file

@ -232,7 +232,7 @@ static int lpc17_40_i2c_start(struct lpc17_40_i2cdev_s *priv)
/* Calculate the approximate timeout */
timeout = ((total_len * (8000000 / CONFIG_USEC_PER_TICK)) / freq) + 1;
timeout = ((total_len * (9000000 / CONFIG_USEC_PER_TICK)) / freq) + 1;
/* Initializes the I2C state machine to a known value */
@ -242,8 +242,6 @@ static int lpc17_40_i2c_start(struct lpc17_40_i2cdev_s *priv)
(uint32_t)priv);
nxsem_wait(&priv->wait);
wd_cancel(priv->timeout);
return priv->nmsg;
}
@ -263,6 +261,7 @@ static void lpc17_40_i2c_stop(struct lpc17_40_i2cdev_s *priv)
priv->base + LPC17_40_I2C_CONSET_OFFSET);
}
wd_cancel(priv->timeout);
nxsem_post(&priv->wait);
}