diff --git a/Cargo.toml b/Cargo.toml
index 341fe2e5d..ad7d17ad7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,7 @@ version = "0.1.1"
 
 [dev-dependencies]
 stm32f30x = "0.6.0"
+cortex-m = "0.6.0"
 futures = "0.1.17"
 
 [features]
diff --git a/src/lib.rs b/src/lib.rs
index 1a903081a..6ce3a1bcd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -694,6 +694,7 @@ pub mod adc;
 pub mod blocking;
 pub mod digital;
 pub mod fmt;
+pub mod mutex;
 pub mod prelude;
 pub mod serial;
 pub mod spi;
diff --git a/src/mutex.rs b/src/mutex.rs
new file mode 100644
index 000000000..9790aac94
--- /dev/null
+++ b/src/mutex.rs
@@ -0,0 +1,234 @@
+//! Generic mutex traits
+//!
+//! The traits in this module allow code to be generic over the mutex type used.
+//! The types implementing these traits must guarantee that access is always
+//! exclusive, even for a `RoMutex`.
+//!
+//! ## Example Implementation
+//!
+//! ### Std
+//! The following code snippet is a possible implementation of the mutex traits
+//! for `std`'s `Mutex`:
+//! ```
+//! # use embedded_hal::mutex;
+//! pub struct StdMutex<T>(std::sync::Mutex<T>);
+//!
+//! impl<T> mutex::Mutex<T> for StdMutex<T> {
+//!     type CreationError = void::Void;
+//!
+//!     fn create(v: T) -> Result<Self, Self::CreationError> {
+//!         Ok(StdMutex(std::sync::Mutex::new(v)))
+//!     }
+//! }
+//!
+//! impl<T> mutex::RwMutex<T> for StdMutex<T> {
+//!     type Error = ();
+//!
+//!     fn lock_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R, Self::Error> {
+//!         // Erase the error type in this example for simplicity
+//!         let mut v = self.0.try_lock().or(Err(()))?;
+//!         Ok(f(&mut v))
+//!     }
+//! }
+//!
+//! // RoMutex is implemented automatically by adding the following:
+//! impl<T> mutex::default::DefaultRo for StdMutex<T> { }
+//!
+//! # fn main() {
+//! // Make use of the mutex:
+//! use embedded_hal::mutex::{Mutex, RoMutex, RwMutex};
+//!
+//! let m = StdMutex::create(123).unwrap();
+//! m.lock_mut(|v| {
+//!     assert_eq!(*v, 123);
+//!     *v = 321;
+//! }).unwrap();
+//! m.lock(|v|
+//!     assert_eq!(*v, 321)
+//! ).unwrap();
+//! # }
+//! ```
+//!
+//! ### `cortex-m`
+//! `cortex-m` uses the `bare-metal` mutex type, an implementation might look
+//! like this:
+//! ```
+//! # use embedded_hal::mutex;
+//! pub struct CortexMMutex<T>(cortex_m::interrupt::Mutex<T>);
+//!
+//! impl<T> mutex::Mutex<T> for CortexMMutex<T> {
+//!     type CreationError = void::Void;
+//!
+//!     fn create(v: T) -> Result<Self, Self::CreationError> {
+//!         Ok(CortexMMutex(cortex_m::interrupt::Mutex::new(v)))
+//!     }
+//! }
+//!
+//! impl<T> mutex::RoMutex<T> for CortexMMutex<T> {
+//!     type Error = void::Void;
+//!
+//!     fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R, Self::Error> {
+//!         Ok(cortex_m::interrupt::free(|cs| {
+//!             let v = self.0.borrow(cs);
+//!             f(v)
+//!         }))
+//!     }
+//! }
+//!
+//! // Implement RwMutex for CortexMMutex<RefCell<T>> automatically:
+//! impl<T> mutex::default::RefCellRw for CortexMMutex<T> { }
+//!
+//! // Add a type alias for convenience
+//! type CortexMMutexRw<T> = CortexMMutex<core::cell::RefCell<T>>;
+//! #
+//! # // Check that implementations actually exist
+//! # fn is_mu<T, M: mutex::Mutex<T>>() { }
+//! # fn is_ro<T, M: mutex::RoMutex<T>>() { }
+//! # fn is_rw<T, M: mutex::RwMutex<T>>() { }
+//! #
+//! # is_mu::<(), CortexMMutex<()>>();
+//! # is_ro::<(), CortexMMutex<()>>();
+//! # is_mu::<(), CortexMMutexRw<()>>();
+//! # is_rw::<(), CortexMMutexRw<()>>();
+//! ```
+
+/// A generic mutex abstraction.
+///
+/// This trait by itself is not that useful, `RoMutex` and `RwMutex` have this
+/// as their common requirement.  See the module root for more info.
+#[cfg(feature = "unproven")]
+pub trait Mutex<T>: Sized {
+    /// Creation Error
+    type CreationError;
+
+    /// Create a new mutex of this type.
+    fn create(v: T) -> Result<Self, Self::CreationError>;
+}
+
+/// A read-only (immutable) mutex.
+///
+/// This means, the value it shares is immutable, but only a single context may
+/// have exclusive access.
+///
+/// `RwMutex`es can implement this trait automatically using
+/// ```
+/// # use embedded_hal::mutex;
+/// # struct MyMutex<T>(T);
+/// impl<T> mutex::default::DefaultRo for MyMutex<T> { }
+/// ```
+#[cfg(feature = "unproven")]
+pub trait RoMutex<T>: Mutex<T> {
+    /// Locking error
+    type Error;
+
+    /// Lock the mutex for the duration of a closure
+    ///
+    /// `lock` will call a closure with an immutable reference to the unlocked
+    /// mutex's value.
+    fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R, Self::Error>;
+}
+
+/// A read-write (mutable) mutex.
+///
+/// This mutex type is similar to the Mutex from `std`.  When you lock it, you
+/// get access to a mutable reference.
+///
+/// This trait can automatically be implemented for `RoMutex<RefCell<T>>` by using
+/// ```
+/// # use embedded_hal::mutex;
+/// # struct MyMutex<T>(T);
+/// impl<T> mutex::default::RefCellRw for MyMutex<T> { }
+/// ```
+#[cfg(feature = "unproven")]
+pub trait RwMutex<T>: Mutex<T> {
+    /// Locking error
+    type Error;
+
+    /// Lock the mutex for the duration of a closure
+    ///
+    /// `lock_mut` will call a closure with a mutable reference to the unlocked
+    /// mutex's value.
+    fn lock_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R, Self::Error>;
+}
+
+/// Blanket implementations for `RoMutex` and `RwMutex`
+///
+/// Any `RwMutex` can trivially implement `RoMutex` as well.  To enable this,
+/// add a line like
+/// ```
+/// # use embedded_hal::mutex;
+/// # struct MyMutex<T>(T);
+/// impl<T> mutex::default::DefaultRo for MyMutex<T> { }
+/// ```
+/// to your mutex definition.
+///
+/// Similarly, a `RoMutex` and a `RefCell` can be used to implement `RwMutex`.
+/// The blanket implementation can be enabled using
+/// ```
+/// # use embedded_hal::mutex;
+/// # struct MyMutex<T>(T);
+/// impl<T> mutex::default::RefCellRw for MyMutex<T> { }
+/// ```
+#[cfg(feature = "unproven")]
+pub mod default {
+    use super::*;
+    use core::cell::RefCell;
+
+    /// Marker trait to enable the default `RoMutex` implementation.
+    ///
+    /// Your mutex type must implement `RwMutex` for this to have an effect!
+    pub trait DefaultRo {}
+
+    // Blanket impl:
+    //   Every read-write mutex is also read-only.  Don't confuse this with an
+    //   RwLock where multiple reads are allowed simultaneously!
+    impl<T, M> RoMutex<T> for M
+    where
+        M: DefaultRo + RwMutex<T>,
+    {
+        type Error = <M as RwMutex<T>>::Error;
+
+        fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R, Self::Error> {
+            self.lock_mut(|v| f(v))
+        }
+    }
+
+    /// Marker trait to enable an implementation of `RwMutex` using `RefCell`s
+    ///
+    /// Your mutex type must implement `RoMutex` for this to have an effect!
+    pub trait RefCellRw {}
+
+    // Blanket impl:
+    //   You can use a RefCell to make an RoMutex read-write.  This means, the
+    //   bare-metal mutex type (which is read-only) can easily be used for
+    //   creating a read-write mutex!
+    //
+    //   This is the wrapper for creation of such a mutex.
+    impl<T, M> Mutex<T> for M
+    where
+        M: RefCellRw + RoMutex<RefCell<T>>,
+    {
+        type CreationError = <M as Mutex<RefCell<T>>>::CreationError;
+
+        fn create(v: T) -> Result<Self, Self::CreationError> {
+            <M as Mutex<RefCell<T>>>::create(RefCell::new(v))
+        }
+    }
+
+    // Blanket impl:
+    //   You can use a RefCell to make an RoMutex read-write.  This means, the
+    //   bare-metal mutex type (which is read-only) can easily be used for
+    //   creating a read-write mutex!
+    //
+    //   This is the actual RwMutex implementation.
+    impl<T, M> RwMutex<T> for M
+    where
+        M: RefCellRw + RoMutex<RefCell<T>> + Mutex<T>,
+    {
+        type Error = <M as RoMutex<RefCell<T>>>::Error;
+
+        fn lock_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R, Self::Error> {
+            self.lock(|v| f(&mut v.borrow_mut()))
+        }
+    }
+}