diff --git a/System/Hardware/BusPirate.hs b/System/Hardware/BusPirate.hs
--- a/System/Hardware/BusPirate.hs
+++ b/System/Hardware/BusPirate.hs
@@ -1,11 +1,13 @@
 {-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}
-                
+
 module System.Hardware.BusPirate
   ( -- * General
     BusPirateM
   , runBusPirate
     -- * I2C mode
-  , module System.Hardware.BusPirate.I2C
+  , runI2c
+    -- * SPI mode
+  , runSpi
   ) where
 
 import Control.Monad (replicateM)
@@ -17,3 +19,12 @@
 
 import System.Hardware.BusPirate.Core
 import System.Hardware.BusPirate.I2C
+import System.Hardware.BusPirate.SPI
+
+-- | Convenient way to run an I2C action
+runI2c :: FilePath -> I2cM a -> IO (Either String a)
+runI2c path action = runBusPirate path $ i2cMode action
+
+-- | Convenient way to run an I2C action
+runSpi :: FilePath -> SpiM a -> IO (Either String a)
+runSpi path action = runBusPirate path $ spiMode action
diff --git a/System/Hardware/BusPirate/Core.hs b/System/Hardware/BusPirate/Core.hs
--- a/System/Hardware/BusPirate/Core.hs
+++ b/System/Hardware/BusPirate/Core.hs
@@ -94,3 +94,22 @@
     
 command :: Word8 -> BusPirateM ()
 command cmd = commandExpect cmd "\x01"
+
+data PeripheralConfig
+    = PConfig { perPower      :: Bool
+              , perPullups    :: Bool
+              , perAux        :: Bool
+              , perChipSelect :: Bool
+              }
+    deriving (Show)
+
+setPeripherals :: PeripheralConfig -> BusPirateM ()
+setPeripherals config = do
+    command $ 0x40
+            + bit 3 (perPower config)
+            + bit 2 (perPullups config)
+            + bit 1 (perAux config)
+            + bit 0 (perChipSelect config)
+  where
+    bit n True = 2^n
+    bit _ _    = 0
diff --git a/System/Hardware/BusPirate/I2C.hs b/System/Hardware/BusPirate/I2C.hs
--- a/System/Hardware/BusPirate/I2C.hs
+++ b/System/Hardware/BusPirate/I2C.hs
@@ -11,9 +11,8 @@
   , ackBit
   , nackBit
   , bulkWrite
-  , writeRead
     -- * Configuration
-  , I2cConfig(..)
+  , PeripheralConfig(..)
   , setConfig
   , I2cSpeed(..)
   , setSpeed
@@ -44,10 +43,13 @@
 
 newtype I2cM a = I2cM (BusPirateM a)
                deriving (Functor, Applicative, Monad, MonadIO)
-        
+
+err :: String -> I2cM a
+err = I2cM . BPM . left
+
 -- | Enter I2C mode and run given action
 i2cMode :: I2cM a -> BusPirateM a
-i2cMode (I2cM m) = commandExpect 0x2 "I2C1" >>  m
+i2cMode (I2cM m) = commandExpect 0x2 "I2C1" >> m
 
 -- | Send a start bit
 startBit :: I2cM ()
@@ -61,11 +63,11 @@
 readByte :: I2cM Word8
 readByte = I2cM $ putByte 0x4 >> getByte
 
--- | Send an ACK 
+-- | Send an ACK
 ackBit :: I2cM ()
 ackBit = I2cM $ command 0x6
 
--- | Send a NACK 
+-- | Send a NACK
 nackBit :: I2cM ()
 nackBit = I2cM $ command 0x7
 
@@ -74,10 +76,10 @@
 
 -- | Write some bytes
 bulkWrite :: ByteString -> I2cM ()
-bulkWrite d 
+bulkWrite d
   | BS.null d = return ()
   | BS.length d > 16 = I2cM $ BPM $ left "Too many bytes"
-  | otherwise = I2cM $ do 
+  | otherwise = I2cM $ do
     command $ fromIntegral $ 0x10 + BS.length d - 1
     put d
     acks <- replicateM (BS.length d) $ toEnum . fromIntegral <$> getByte
@@ -87,41 +89,31 @@
                    bytes = if length nacks > 1 then "bytes" else "byte"
                in fail $ "Nack after "++bytes++" "++nacks'++" during bulkWrite of "++show d
 
-data I2cConfig = I2cConfig { i2cPower      :: Bool
-                           , i2cPullups    :: Bool
-                           , i2cAux        :: Bool
-                           , i2cChipSelect :: Bool
-                           }
-               deriving (Show)
 
--- | Set Bus Pirate I2C configuration bits
-setConfig :: I2cConfig -> I2cM ()
-setConfig config = I2cM $ 
-    command $ 0x40
-            + bit 3 (i2cPower config)
-            + bit 2 (i2cPullups config)
-            + bit 1 (i2cAux config)
-            + bit 0 (i2cChipSelect config)
-  where
-    bit n True = 2^n
-    bit _ _    = 0
+-- | Set Bus Pirate peripheral configuration bits
+setConfig :: PeripheralConfig -> I2cM ()
+setConfig config = I2cM $ setPeripherals config
 
+-- | I2C bus speed
 data I2cSpeed = I2c_5kHz
               | I2c_50kHz
               | I2c_100kHz
               | I2c_400kHz
               deriving (Show, Eq, Ord, Enum, Bounded)
-              
+
 -- | Set I2C bus speed
 setSpeed :: I2cSpeed -> I2cM ()
 setSpeed speed = I2cM $ command $ fromIntegral $ 0x60 + fromEnum speed
 
 -- | Send Start bit, write some bytes, then read some bytes (ACKing
 -- each until the last), then send a stop bit
+--
+-- This is very likely broken as the command structure itself appears
+-- to be horribly broken, requiring a conditional read of a status byte.
 writeRead :: ByteString -> Int -> I2cM ByteString
-writeRead send recv 
-  | BS.length send > 0xffff = error "Too large send request"
-  | recv > 0xffff           = error "Too large recieve request"
+writeRead send recv
+  | BS.length send > 0xffff = err "Too large send request"
+  | recv > 0xffff           = err "Too large recieve request"
   | otherwise               = I2cM $ do
     putByte 0x8
     putWord16 $ fromIntegral $ BS.length send
diff --git a/System/Hardware/BusPirate/SPI.hs b/System/Hardware/BusPirate/SPI.hs
new file mode 100644
--- /dev/null
+++ b/System/Hardware/BusPirate/SPI.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}
+
+module System.Hardware.BusPirate.SPI
+  ( -- * Types
+    SpiM
+  , spiMode
+    -- * Bus operations
+  , bulkTransfer
+  , setCS
+    -- * Configuration
+  , PeripheralConfig(..)
+  , setConfig
+  , SpiSpeed(..)
+  , setSpeed
+  ) where
+
+import Control.Applicative
+import Control.Monad (replicateM)
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Either
+import Data.Word
+
+import qualified Data.ByteString as BS
+import Data.ByteString (ByteString)
+
+import System.Hardware.BusPirate.Core
+
+newtype SpiM a = SpiM (BusPirateM a)
+               deriving (Functor, Applicative, Monad, MonadIO)
+
+err :: String -> SpiM a
+err = SpiM . BPM . left
+
+-- | Enter I2C mode and run given action
+spiMode :: SpiM a -> BusPirateM a
+spiMode (SpiM m) = commandExpect 0x1 "SPI1" >> m
+
+-- | Set chip select
+setCS :: Bool -> SpiM ()
+setCS True = SpiM $ command 0x21
+setCS False = SpiM $ command 0x20
+
+bulkTransfer :: ByteString -> SpiM ByteString
+bulkTransfer d
+  | BS.null d = return BS.empty
+  | BS.length d > 16 = SpiM $ BPM $ left "Too many bytes"
+  | otherwise = SpiM $ do
+    command $ fromIntegral $ 0x10 + BS.length d - 1
+    put d
+    reply <- replicateM (BS.length d) $ getByte
+    return $ BS.pack reply
+
+-- | Set Bus Pirate peripheral configuration bits
+setConfig :: PeripheralConfig -> SpiM ()
+setConfig config = SpiM $ setPeripherals config
+
+data SpiSpeed
+    = Spi30KHz
+    | Spi125KHz
+    | Spi250KHz
+    | Spi1MHz
+    | Spi2MHz
+    | Spi2_6MHz
+    | Spi4MHz
+    | Spi8MHz
+    deriving (Show, Ord, Eq, Enum, Bounded)
+
+-- | Set bus speed
+setSpeed :: SpiSpeed -> SpiM ()
+setSpeed speed = SpiM $ command $ 0x6 + fromIntegral (fromEnum speed)
+
+-- | Write-then-read
+--
+-- This is very likely broken as the command structure itself appears
+-- to be horribly broken, requiring a conditional read of a status byte.
+writeRead :: Bool -> ByteString -> Word16 -> SpiM ByteString
+writeRead toggleCS write readLen
+  | readLen >= 4096 = err "Read length too long"
+  | BS.length write >= 4096 = err "Write length too long"
+  | otherwise = SpiM $ do
+    putByte $ if toggleCS then 0x4 else 0x5
+    putWord16 $ fromIntegral $ BS.length write
+    putWord16 readLen
+    status <- getByte
+    case status of
+      0 -> BPM $ left "Data too long"
+      1 -> do put write
+              get (fromIntegral readLen)
diff --git a/bus-pirate.cabal b/bus-pirate.cabal
--- a/bus-pirate.cabal
+++ b/bus-pirate.cabal
@@ -1,5 +1,5 @@
 name:                bus-pirate
-version:             0.5
+version:             0.6
 synopsis:            Haskell interface to the Bus Pirate binary interface
 homepage:            http://www.github.com/bgamari/bus-pirate
 license:             BSD3
@@ -20,6 +20,7 @@
 library
   exposed-modules:     System.Hardware.BusPirate
                        System.Hardware.BusPirate.I2C
+                       System.Hardware.BusPirate.SPI
   other-modules:       System.Hardware.BusPirate.Core
   build-depends:       base >=4.6 && <4.8,
                        serialport >=0.4 && <0.5,
