From 0be87af99d4b1bee2110e1378a2fb9767d6bf2c2 Mon Sep 17 00:00:00 2001 From: Augusto Fraga Giachero Date: Mon, 2 Mar 2020 16:34:54 -0300 Subject: [PATCH] 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. --- arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c b/arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c index f6e43d3c31..2052e7b40d 100644 --- a/arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c +++ b/arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c @@ -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); }