{-|
  Module      : $Header$
  Copyright   : (c) 2014 Edward O'Callaghan
  License     : LGPL-2.1
  Maintainer  : eocallaghan@alterapraxis.com
  Stability   : provisional
  Portability : portable

  This module deals with GPIO configuration handling.

  The following example illustrates the setup of GPIO's
  for timestamping in the usual, read, modify, write
  sequence:

  @
   gpios <- bladeRFConfigGPIORead dev
   case gpios of
     Left e -> throwIO e
     Right gpios -> do
       putStrLn \"========= GPIO Dump =========\"
       mapM_ putStrLn $ debugBladeRFGPIOFlags gpios
       bladeRFConfigGPIOWrite dev $ GPIO_TIMESTAMP : gpios
  @
-}

{-# LANGUAGE Trustworthy #-}
module LibBladeRF.Gpio ( BladeRFGPIOFlags(..)
                       , debugBladeRFGPIOFlags
                       , bladeRFConfigGPIORead
                       , bladeRFConfigGPIOWrite
                       ) where

import Foreign
import Foreign.C.Types
import Foreign.C.String

import Data.Maybe
import Data.Tuple

import Bindings.LibBladeRF
import LibBladeRF.LibBladeRF
import LibBladeRF.Types


-- | BladeRF GPIO Flag Type.
data BladeRFGPIOFlags = GPIO_LMS_RX_ENABLE          -- ^ Enable LMS receive.
                                                    --   N.B. This bit is set/cleared by 'LibBladeRF.Utils.bladeRFEnableModule'
                      | GPIO_LMS_TX_ENABLE          -- ^ Enable LMS transmit.
                                                    --   N.B. This bit is set/cleared by 'LibBladeRF.Utils.bladeRFEnableModule'
                      | GPIO_TX_LB_ENABLE           -- ^ Switch to use TX low band (300MHz - 1.5GHz).
                                                    --   N.B. This is set using 'LibBladeRF.Frequency.bladeRFSetFrequency'.
                      | GPIO_TX_HB_ENABLE           -- ^ Switch to use TX high band (1.5GHz - 3.8GHz).
                                                    --   N.B. This is set using 'LibBladeRF.Frequency.bladeRFSetFrequency'.
                      | GPIO_COUNTER_ENABLE         -- ^ Counter mode enable.
                                                    --
                                                    --   Setting this bit to 1 instructs the FPGA to replace the (I, Q) pair in
                                                    --   sample data with an incrementing, little-endian, 32-bit counter value. A
                                                    --   0 in bit specifies that sample data should be sent (as normally done).
                                                    --
                                                    --   This feature is useful when debugging issues involving dropped samples.
                      | GPIO_RX_LB_ENABLE           -- ^ Switch to use RX low band (300M - 1.5GHz).
                                                    --   N.B. This is set using 'LibBladeRF.Frequency.bladeRFSetFrequency'.
                      | GPIO_RX_HB_ENABLE           -- ^ Switch to use RX high band (1.5GHz - 3.8GHz).
                                                    --   N.B. This is set using 'LibBladeRF.Frequency.bladeRFSetFrequency'.
                      | GPIO_FEATURE_SMALL_DMA_XFER -- ^ This GPIO bit configures the FPGA to use smaller DMA
                                                    --   transfers (256 cycles instead of 512). This is required
                                                    --   when the device is not connected at Super Speed (i.e., when
                                                    --   it is connected at High Speed).
                                                    --
                                                    --   However, the caller need not set this in 'bladeRFConfigGPIOWrite' calls.
                                                    --   The library will set this as needed; callers generally
                                                    --   do not need to be concerned with setting/clearing this bit.
                      | GPIO_TIMESTAMP              -- ^ Enable-bit for timestamp counter in the FPGA.
                      | GPIO_TIMESTAMP_DIV2         -- ^ Timestamp 2x divider control.
                                                    --
                                                    --   By default (value = 0), the sample counter is incremented with I and Q,
                                                    --   yielding two counts per sample.
                                                    --
                                                    --   Set this bit to 1 to enable a 2x timestamp divider, effectively
                                                    --   achieving 1 timestamp count per sample.
                      deriving (Eq)

instance Enum BladeRFGPIOFlags where
  fromEnum = fromJust . flip lookup gpios
  toEnum   = fromJust . flip lookup (map swap gpios)

gpios = [ (GPIO_LMS_RX_ENABLE, c'BLADERF_GPIO_LMS_RX_ENABLE)
        , (GPIO_LMS_TX_ENABLE, c'BLADERF_GPIO_LMS_TX_ENABLE)
        , (GPIO_TX_LB_ENABLE, c'BLADERF_GPIO_TX_LB_ENABLE)
        , (GPIO_TX_HB_ENABLE, c'BLADERF_GPIO_TX_HB_ENABLE)
        , (GPIO_COUNTER_ENABLE, c'BLADERF_GPIO_COUNTER_ENABLE)
        , (GPIO_RX_LB_ENABLE, c'BLADERF_GPIO_RX_LB_ENABLE)
        , (GPIO_RX_HB_ENABLE, c'BLADERF_GPIO_RX_HB_ENABLE)
        , (GPIO_FEATURE_SMALL_DMA_XFER, c'BLADERF_GPIO_FEATURE_SMALL_DMA_XFER)
        , (GPIO_TIMESTAMP, c'BLADERF_GPIO_TIMESTAMP)
        , (GPIO_TIMESTAMP_DIV2, c'BLADERF_GPIO_TIMESTAMP_DIV2)
        ]

-- | Useful helper function to decode GPIO flags into strings
--
--   Example:
--
--   @
--    gpios <- bladeRFConfigGPIORead dev
--    case gpios of
--      Left e -> throwIO e
--      Right g -> mapM_ putStrLn $ debugBladeRFGPIOFlags g
--   @
--
debugBladeRFGPIOFlags :: [BladeRFGPIOFlags] -> [String]
debugBladeRFGPIOFlags  = map fts
  where fts f | f == GPIO_LMS_RX_ENABLE          = " [GPIO flag set] " ++ "Enable LMS receive."
              | f == GPIO_LMS_TX_ENABLE          = " [GPIO flag set] " ++ "Enable LMS transmit."
              | f == GPIO_TX_LB_ENABLE           = " [GPIO flag set] " ++ "Switch to use TX low band (300MHz - 1.5GHz)."
              | f == GPIO_TX_HB_ENABLE           = " [GPIO flag set] " ++ "Switch to use TX high band (1.5GHz - 3.8GHz)."
              | f == GPIO_COUNTER_ENABLE         = " [GPIO flag set] " ++ "Counter mode enable."
              | f == GPIO_RX_LB_ENABLE           = " [GPIO flag set] " ++ "Switch to use RX low band (300M - 1.5GHz)."
              | f == GPIO_RX_HB_ENABLE           = " [GPIO flag set] " ++ "Switch to use RX high band (1.5GHz - 3.8GHz)."
              | f == GPIO_FEATURE_SMALL_DMA_XFER = " [GPIO flag set] " ++ "Configures the FPGA to use smaller DMA transfers."
              | f == GPIO_TIMESTAMP              = " [GPIO flag set] " ++ "Enable-bit for timestamp counter in the FPGA."
              | f == GPIO_TIMESTAMP_DIV2         = " [GPIO flag set] " ++ "Timestamp 2x divider control."

-- | Read a configuration GPIO register.
bladeRFConfigGPIORead :: DeviceHandle                              -- ^ Device handle
                      -> IO (BladeRFReturnType [BladeRFGPIOFlags]) -- ^ Read data
bladeRFConfigGPIORead dev = alloca $ \pv -> do
  ret <- c'bladerf_config_gpio_read (unDeviceHandle dev) pv
  if ret < 0 then (return . Left . toEnum . fromIntegral) ret -- C ret code to typed error
  else do gpior <- peek pv
          (return . Right . wordToFlags . fromIntegral) gpior
  where wordToFlags w = [e | (e, bit) <- gpios, bit .&. w /= 0]

-- | Write a configuration GPIO register.
--
-- Callers should be sure to perform a read-modify-write sequence to avoid
-- accidentally clearing other GPIO bits that may be set by the library internally.
bladeRFConfigGPIOWrite :: DeviceHandle       -- ^ Device handle
                       -> [BladeRFGPIOFlags] -- ^ Data to write to GPIO register
                       -> IO (BladeRFReturnType ())
bladeRFConfigGPIOWrite dev v = do
  ret <- c'bladerf_config_gpio_write (unDeviceHandle dev) value
  return $ bladeRFErrorTy ret
  where
    value = foldr1 (.|.) flags
    flags = map (fromIntegral . fromEnum) v