{-# LANGUAGE ForeignFunctionInterface #-}

module System.PIO.Linux.SPI.Raw (
  SPIMode(..),
  Bits,
  Speed,
  transferTxRx1,
  transferTxRx2,
  transferTx,
  transferRx,
  getMode,
  setMode,
  getLsbFirst,
  setLsbFirst,
  getBitsPerWord,
  setBitsPerWord,
  getMaxSpeedHz,
  setMaxSpeedHz,
  ) where

import Foreign.Ptr(Ptr)
import Data.Word(Word8,Word16,Word32)
import System.PIO.Linux(FileDescriptor)
import Foreign.Marshal.Alloc(alloca)
import Foreign.Storable(peek)
import Foreign.C.Error(throwErrno)


type Bits  = Word8
type Speed = Word32
type Delay = Word16
type CSChange = Bool


foreign import ccall "spi_mode_0" spi_mode0 :: Word8
foreign import ccall "spi_mode_1" spi_mode1 :: Word8
foreign import ccall "spi_mode_2" spi_mode2 :: Word8
foreign import ccall "spi_mode_3" spi_mode3 :: Word8

foreign import ccall "spi_transfer_tx_rx_1" spi_transfer_tx_rx_1
  :: Int -> Ptr a -> Int -> Word8 -> Word32 -> Word16 -> Word8 -> IO Int
foreign import ccall "spi_transfer_tx_rx_2" spi_transfer_tx_rx_2
  :: Int -> Ptr a -> Int -> Ptr a -> Int -> Word8 -> Word32 -> Word16 -> Word8 -> IO Int
foreign import ccall "spi_transfer_tx_1" spi_transfer_tx_1
  :: Int -> Ptr a -> Int -> Word8 -> Word32 -> Word16 -> Word8 -> IO Int
foreign import ccall "spi_transfer_rx_1" spi_transfer_rx_1
  :: Int -> Ptr a -> Int -> Word8 -> Word32 -> Word16 -> Word8 -> IO Int

foreign import ccall "spi_get_mode" spi_get_mode :: Int -> Ptr Word8 -> IO Int
foreign import ccall "spi_set_mode" spi_set_mode :: Int -> Word8 -> IO Int

foreign import ccall "spi_get_lsb_first" spi_get_lsb_first :: Int -> Ptr Word8 -> IO Int
foreign import ccall "spi_set_lsb_first" spi_set_lsb_first :: Int -> Word8 -> IO Int

foreign import ccall "spi_get_bits_per_word" spi_get_bits_per_word :: Int -> Ptr Word8 -> IO Int
foreign import ccall "spi_set_bits_per_word" spi_set_bits_per_word :: Int -> Word8 -> IO Int

foreign import ccall "spi_get_max_speed_hz" spi_get_max_speed_hz :: Int -> Ptr Word32 -> IO Int
foreign import ccall "spi_set_max_speed_hz" spi_set_max_speed_hz :: Int -> Word32 -> IO Int



data SPIMode = Mode0
             | Mode1
             | Mode2
             | Mode3
              
                                        
instance Enum SPIMode where
  
  toEnum n | (fromIntegral n) == spi_mode0 = Mode0
           | (fromIntegral n) == spi_mode1 = Mode1
           | (fromIntegral n) == spi_mode2 = Mode2
           | (fromIntegral n) == spi_mode3 = Mode3
           | otherwise  = error "toEnum: convert error"

  fromEnum Mode0 = (fromIntegral spi_mode0)
  fromEnum Mode1 = (fromIntegral spi_mode1)
  fromEnum Mode2 = (fromIntegral spi_mode2)
  fromEnum Mode3 = (fromIntegral spi_mode3)



-- | 'transferTxRx1' @fd@ @buf@ @count@ @bits@ @speed@ @delay@ @csChange@ transfer @count@ 8-bit bytes from the
-- buffer @buf@ to the descriptor @fd@.
transferTxRx1 :: FileDescriptor -> Ptr a -> Int -> Bits -> Speed -> Delay -> CSChange -> IO ()
transferTxRx1 fd buf bufLen bits speed delay csChange =
  spi_transfer_tx_rx_1 fd buf bufLen bits speed delay (fromIntegral $ fromEnum csChange)
  >>= \rc -> case rc of
    -1 -> throwErrno "transferTxRx1"
    otherwize -> return ()
    

transferTxRx2 :: FileDescriptor -> Ptr a -> Int -> Ptr a -> Int -> Bits -> Speed -> Delay -> CSChange -> IO ()
transferTxRx2 fd txBuf txBufLen rxBuf rxBufLen bits speed delay csChange =
  spi_transfer_tx_rx_2 fd txBuf txBufLen rxBuf rxBufLen bits speed delay (fromIntegral $ fromEnum csChange)
  >>= \rc -> case rc of
    -1 -> throwErrno "transferTxRx2"
    otherwize -> return ()
  

transferTx :: FileDescriptor -> Ptr a -> Int -> Bits -> Speed -> Delay -> CSChange -> IO ()
transferTx fd buf bufLen bits speed delay csChange =
  spi_transfer_tx_1 fd buf bufLen bits speed delay (fromIntegral $ fromEnum csChange)
  >>= \rc -> case rc of
    -1 -> throwErrno "transferTx"
    otherwize -> return ()
    

transferRx :: FileDescriptor -> Ptr a -> Int -> Bits -> Speed -> Delay -> CSChange -> IO ()
transferRx fd buf bufLen bits speed delay csChange =
  spi_transfer_rx_1 fd buf bufLen bits speed delay (fromIntegral $ fromEnum csChange)
  >>= \rc -> case rc of
    -1 -> throwErrno "transferRx"
    otherwize -> return ()



-- | Computation 'getMode' @fd@ gets the spi mode for
-- descriptor @fd@.
getMode :: FileDescriptor -> IO SPIMode
getMode fd = alloca $
             \p -> spi_get_mode fd p >>= \rc -> case rc of
               -1 -> throwErrno "getMode"
               otherwize -> do
                 mode <- peek p
                 return $ toEnum $ fromIntegral mode


-- | Computation 'setMode' @fd@ @mode@ sets the spi mode for
-- descriptor @fd@.
setMode :: FileDescriptor -> SPIMode -> IO ()
setMode fd mode =
  spi_set_mode fd (fromIntegral $ fromEnum mode)
  >>= \rc -> case rc of
    -1 -> throwErrno "setMode"
    otherwize -> return ()


-- | Computation 'getLsbFirst' @fd@ gets the LBS-first flag for
-- descriptor @fd@.
getLsbFirst :: FileDescriptor -> IO Bool
getLsbFirst fd = alloca $
                 \p -> spi_get_lsb_first fd p >>= \rc -> case rc of
                   -1 -> throwErrno "getLsbFirst"
                   otherwize -> do
                     flag <- peek p
                     return $ toEnum $ fromIntegral flag


-- | Computation 'setLsbFirst' @fd@ @flag@ sets the LBS-first flag for
-- descriptor @fd@.
setLsbFirst :: FileDescriptor -> Bool -> IO ()
setLsbFirst fd flag =
  spi_set_lsb_first fd (fromIntegral $ fromEnum flag)
  >>= \rc -> case rc of
    -1 -> throwErrno "setLsbFirst"
    otherwize -> return ()


-- | Computation 'getBitsPerWord' @fd@ gets the number
-- of bits in each SPI transfer word for
-- descriptor @fd@.
getBitsPerWord :: FileDescriptor -> IO Bits
getBitsPerWord fd = alloca $
  \p -> spi_get_bits_per_word fd p >>= \rc -> case rc of
    -1 -> throwErrno "getBitsPerWord"
    otherwize -> peek p


-- | Computation 'setBitsPerWord' @fd@ @bits@ sets the number
-- of bits in each SPI transfer word for
-- descriptor @fd@.
setBitsPerWord :: FileDescriptor -> Bits -> IO ()
setBitsPerWord fd bits = spi_set_bits_per_word fd bits
  >>= \rc -> case rc of
    -1 -> throwErrno "setBitsPerWord"
    otherwize -> return ()


-- | Computation 'getMaxSpeedHz' @fd@ gets the maximum
-- SPI transfer speed, in Hz for
-- descriptor @fd@.
getMaxSpeedHz :: FileDescriptor -> IO Speed
getMaxSpeedHz fd = alloca $
  \p -> spi_get_max_speed_hz fd p >>= \rc -> case rc of
    -1 -> throwErrno "getMaxSpeedHz"
    otherwize -> peek p


-- | Computation 'setMaxSpeedHz' @fd@ @speed@ sets the maximum
-- SPI transfer speed, in Hz for
-- descriptor @fd@.
setMaxSpeedHz :: FileDescriptor -> Speed -> IO ()
setMaxSpeedHz fd speed = spi_set_max_speed_hz fd speed
  >>= \rc -> case rc of
    -1 -> throwErrno "setMaxSpeedHz"
    otherwize -> return ()