From 0bfeec7f4b93911fd18c0e16a98cc5fbd82a5e38 Mon Sep 17 00:00:00 2001
From: Erin Tomson <erin@integerlabs.net>
Date: Fri, 24 Jul 2015 23:08:36 -0700
Subject: [PATCH] Fix two bugs that can cause deadlock conditions when i2c bus
 errors occur.

The first occurs when starting a read transaction from a slave that doesn't respond. The code would wait until the SB (slave on bus) bit is set in the INTFLAGS register, but when a nack occurs that never happens so we're stuck in an infinite loop. The fix is to also look for the MB flag to be set. If it is, issue a stop condition and return.

The second happens when a bus error (ie, an illegal stop condition) occurs while sending data as a master. In that case we are waiting for the MB (master on bus) flag to be set. When a bus error occurs that never happens, so again we end up in an infinite loop. The fix here is to also look for the BUSERR flag to be set. If it is, return an error condition.
---
 cores/arduino/SERCOM.cpp | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp
index 7d7e7f9d..2cfb172a 100644
--- a/cores/arduino/SERCOM.cpp
+++ b/cores/arduino/SERCOM.cpp
@@ -493,6 +493,12 @@ bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag
   {
     while( !sercom->I2CM.INTFLAG.bit.SB )
     {
+        // If the slave NACKS the address, the MB bit will be set.
+        // In that case, send a stop condition and return false.
+        if (sercom->I2CM.INTFLAG.bit.MB) {
+            sercom->I2CM.CTRLB.bit.CMD = 3; // Stop condition
+            return false;
+        }
       // Wait transmission complete
     }
 
@@ -518,7 +524,14 @@ bool SERCOM::sendDataMasterWIRE(uint8_t data)
   sercom->I2CM.DATA.bit.DATA = data;
 
   //Wait transmission successful
-  while(!sercom->I2CM.INTFLAG.bit.MB);
+  while(!sercom->I2CM.INTFLAG.bit.MB) {
+
+    // If a bus error occurs, the MB bit may never be set.
+    // Check the bus error bit and bail if it's set.
+    if (sercom->I2CM.STATUS.bit.BUSERR) {
+      return false;
+    }
+  }
 
   //Problems on line? nack received?
   if(sercom->I2CM.STATUS.bit.RXNACK)
-- 
GitLab