aboutsummaryrefslogtreecommitdiff
path: root/libraries/SPI
diff options
context:
space:
mode:
authorCristian Maglie <c.maglie@bug.st>2014-11-18 20:03:23 +0100
committerCristian Maglie <c.maglie@bug.st>2014-11-25 15:56:11 +0100
commit1e699c360006f62ba75f123b093facaa776e2398 (patch)
tree1a1cc4dff6f5986575efba58446340378ad9fd92 /libraries/SPI
parent00f5ed6b4516f5f39576089b5367f31e15fa6998 (diff)
Fix atomicity issues in SPI::beginTransaction and SPI::endTransaction (Andrew Kroll)
Previously, it could happen that SPI::beginTransaction was interrupted by an ISR, while it is changing the SPI_AVR_EIMSK register or interruptSave variable (it seems that there is a small window after changing SPI_AVR_EIMSK where an interrupt might still occur). If this happens, interruptSave is overwritten with an invalid value, permanently disabling the pin interrupts. To prevent this, disable interrupts globally while changing these values.
Diffstat (limited to 'libraries/SPI')
-rw-r--r--libraries/SPI/SPI.h22
1 files changed, 20 insertions, 2 deletions
diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h
index 2f9b565..cee618c 100644
--- a/libraries/SPI/SPI.h
+++ b/libraries/SPI/SPI.h
@@ -23,6 +23,13 @@
// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
#define SPI_HAS_NOTUSINGINTERRUPT 1
+// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version.
+// This way when there is a bug fix you can check this define to alert users
+// of your code if it uses better version of this library.
+// This also implies everything that SPI_HAS_TRANSACTION as documented above is
+// available too.
+#define SPI_ATOMIC_VERSION 1
+
// Uncomment this line to add detection of mismatched begin/end transactions.
// A mismatch occurs if other libraries fail to use SPI.endTransaction() for
// each SPI.beginTransaction(). Connect an LED to this pin. The LED will turn
@@ -170,17 +177,21 @@ public:
// and configure the correct settings.
inline static void beginTransaction(SPISettings settings) {
if (interruptMode > 0) {
+ uint8_t sreg = SREG;
+ noInterrupts();
+
#ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
interruptSave = SPI_AVR_EIMSK;
SPI_AVR_EIMSK &= ~interruptMask;
+ SREG = sreg;
} else
#endif
{
- interruptSave = SREG;
- cli();
+ interruptSave = sreg;
}
}
+
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
@@ -188,6 +199,7 @@ public:
}
inTransactionFlag = 1;
#endif
+
SPCR = settings.spcr;
SPSR = settings.spsr;
}
@@ -253,10 +265,16 @@ public:
}
inTransactionFlag = 0;
#endif
+
if (interruptMode > 0) {
#ifdef SPI_AVR_EIMSK
+ uint8_t sreg = SREG;
+ #endif
+ noInterrupts();
+ #ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
SPI_AVR_EIMSK = interruptSave;
+ SREG = sreg;
} else
#endif
{