aboutsummaryrefslogtreecommitdiff
path: root/hsm-gpio/Hsm/GPIO.hs
diff options
context:
space:
mode:
Diffstat (limited to 'hsm-gpio/Hsm/GPIO.hs')
-rw-r--r--hsm-gpio/Hsm/GPIO.hs284
1 files changed, 141 insertions, 143 deletions
diff --git a/hsm-gpio/Hsm/GPIO.hs b/hsm-gpio/Hsm/GPIO.hs
index dd69122..2bcf3ed 100644
--- a/hsm-gpio/Hsm/GPIO.hs
+++ b/hsm-gpio/Hsm/GPIO.hs
@@ -1,163 +1,161 @@
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeFamilies #-}
module Hsm.GPIO
- ( G.active
- , G.inactive
- , G.LineRequest
- , GPIO(..)
+ ( GPIOPin (..)
+ , GPIO
, setPins
, setAllPins
- , allocateGPIO
- ) where
+ , runGPIO
+ )
+where
-import Control.IO.Region (Region, alloc, alloc_, defer, free)
import Control.Monad (forM_, void)
-import Data.ByteString (useAsCString)
-import Data.Text (Text, pack)
-import Data.Text.Encoding (encodeUtf8)
-import Data.Vector.Storable qualified as V
-import Foreign.C.Types (CSize(CSize), CUInt(CUInt))
+import Data.Vector.Storable (fromList, replicate, unsafeWith)
+import Effectful (Dispatch (Static), DispatchOf, Eff, IOE, liftIO, (:>))
+import Effectful.Dispatch.Static
+ ( SideEffects (WithSideEffects)
+ , StaticRep
+ , evalStaticRep
+ , getStaticRep
+ , unsafeEff_
+ )
+import Effectful.Resource (Resource, allocateEff, releaseEff)
+import Foreign.C.String (withCString)
+import Foreign.C.Types (CSize (CSize), CUInt)
import Foreign.Ptr (Ptr)
-import Hsm.GPIO.Lib qualified as G
-import Hsm.Log qualified as L
+import Hsm.Core.Serial (makeSerial)
+import Hsm.GPIO.FFI
+ ( LineRequest
+ , LineValue
+ , chipClose
+ , chipOpen
+ , chipRequestLines
+ , inactive
+ , lineConfigAddLineSettings
+ , lineConfigFree
+ , lineConfigNew
+ , lineRequestRelease
+ , lineRequestSetValue
+ , lineRequestSetValues
+ , lineSettingsFree
+ , lineSettingsNew
+ , lineSettingsSetDirection
+ , lineSettingsSetOutputValue
+ , output
+ , requestConfigFree
+ , requestConfigNew
+ , requestConfigSetConsumer
+ )
+import Hsm.Log (Log, Severity (Info, Trace), logMsg)
+import Prelude hiding (replicate)
-logMsg :: Text -> IO ()
-logMsg = L.logMsg ["gpio"]
+$(makeSerial "GPIO" "Pin" "pinLine" ''CUInt $ [2 .. 17] <> [20 .. 27])
-data GPIO
- = GPIO2
- | GPIO3
- | GPIO4
- | GPIO5
- | GPIO6
- | GPIO7
- | GPIO8
- | GPIO9
- | GPIO10
- | GPIO11
- | GPIO12
- | GPIO13
- | GPIO14
- | GPIO15
- | GPIO16
- | GPIO17
- -- | GPIO18 -- reserved for PWM
- -- | GPIO19 -- reserved for PWM
- | GPIO20
- | GPIO21
- | GPIO22
- | GPIO23
- | GPIO24
- | GPIO25
- | GPIO26
- | GPIO27
- deriving (Bounded, Enum, Show)
-
-pinLine :: GPIO -> CUInt
-pinLine = CUInt . read . drop 4 . show
-
-allPins :: [GPIO]
+allPins :: [GPIOPin]
allPins = [minBound .. maxBound]
allLines :: [CUInt]
allLines = pinLine <$> allPins
-setPins :: Ptr G.LineRequest -> [GPIO] -> G.LineValue -> IO ()
-setPins lineRequest pins lineValue = do
- logMsg
- $ "Setting pin(s) "
- <> pack (show pins)
- <> " to state "
- <> pack (show lineValue)
- forM_ pins $ \pin -> G.lineRequestSetValue lineRequest (pinLine pin) lineValue
+pinCount :: Int
+pinCount = length allPins
+
+data GPIO (a :: * -> *) (b :: *)
+
+type instance DispatchOf GPIO = Static WithSideEffects
+
+newtype instance StaticRep GPIO
+ = GPIO (Ptr LineRequest)
+
+setPins :: (GPIO :> es, Log "gpio" :> es) => [GPIOPin] -> LineValue -> Eff es ()
+setPins pins lineValue = do
+ GPIO lineRequest <- getStaticRep
+ logMsg Trace $ "Setting pin(s) " <> show pins <> " to " <> show lineValue
+ forM_ pins $ \pin ->
+ unsafeEff_ $ lineRequestSetValue lineRequest (pinLine pin) lineValue
-setAllPins :: Ptr G.LineRequest -> G.LineValue -> IO ()
-setAllPins lineRequest lineValue = do
- logMsg
- $ "Setting all pins "
- <> pack (show allPins)
- <> " to state "
- <> pack (show lineValue)
- void
- $ V.unsafeWith (V.replicate (length allPins) lineValue)
- $ G.lineRequestSetValues lineRequest
+setAllPins :: (GPIO :> es, Log "gpio" :> es) => LineValue -> Eff es ()
+setAllPins lineValue = do
+ GPIO lineRequest <- getStaticRep
+ logMsg Trace $ "Setting all pins " <> show allPins <> " to " <> show lineValue
+ unsafeEff_ . unsafeWith (replicate pinCount lineValue) $
+ void . lineRequestSetValues lineRequest
-allocateGPIO :: Region -> Text -> IO (Ptr G.LineRequest)
-allocateGPIO region consumer = do
- (chip, chipKey) <- allocateChip
- (lineSettings, lineSettingsKey) <- allocateLineSettings
- (lineConfig, lineConfigKey) <- allocateLineConfig lineSettings
- (requestConfig, requestConfigKey) <- allocateRequestConfig
- lineRequest <- allocateLineRequest chip requestConfig lineConfig
- free requestConfigKey
- free lineConfigKey
- free lineSettingsKey
- free chipKey
- defer region $ setAllPins lineRequest G.inactive
- return lineRequest
+runGPIO
+ :: (IOE :> es, Log "gpio" :> es, Resource :> es)
+ => String
+ -> Eff (GPIO : es) a
+ -> Eff es a
+runGPIO consumer action = do
+ (chipKey, chip) <- chipBracket
+ (lineSettingsKey, lineSettings) <- lineSettingsBracket
+ (lineConfigKey, lineConfig) <- lineConfigBracket lineSettings
+ (requestConfigKey, requestConfig) <- requestConfigBracket
+ (_, lineRequest) <- lineRequestBracket chip requestConfig lineConfig
+ releaseEff requestConfigKey
+ releaseEff lineConfigKey
+ releaseEff lineSettingsKey
+ releaseEff chipKey
+ evalStaticRep (GPIO lineRequest) action
where
- chipPath = "/dev/gpiochip0"
- -- GPIO chip
- chipOpen = do
- logMsg $ "Opening GPIO chip " <> chipPath
- useAsCString (encodeUtf8 chipPath) G.chipOpen
- chipClose chip = do
- logMsg $ "Closing GPIO chip " <> chipPath
- G.chipClose chip
- allocateChip = alloc region chipOpen chipClose
- -- Line settings
- lineSettingsNew = do
- logMsg "Allocating line settings"
- lineSettings <- G.lineSettingsNew
- logMsg $ "With direction set to " <> pack (show G.output)
- void $ G.lineSettingsSetDirection lineSettings G.output
- logMsg $ "With output set to " <> pack (show G.inactive)
- void $ G.lineSettingsSetOutputValue lineSettings G.inactive
- return lineSettings
- lineSettingsFree lineSettings = do
- logMsg "Freeing line settings"
- G.lineSettingsFree lineSettings
- allocateLineSettings = alloc region lineSettingsNew lineSettingsFree
- -- Line config
- lineConfigNew lineSettings = do
- logMsg "Allocating line config"
- logMsg $ "With GPIO pins " <> pack (show allPins)
- lineConfig <- G.lineConfigNew
- void
- $ V.unsafeWith (V.fromList allLines)
- $ \pinsVector ->
- G.lineConfigAddLineSettings
+ chipBracket = allocateEff chipAlloc chipDealloc
+ where
+ chipPath = "/dev/gpiochip0"
+ chipAlloc = do
+ logMsg Info $ "Opening GPIO chip " <> chipPath
+ liftIO $ withCString chipPath chipOpen
+ chipDealloc chip = do
+ logMsg Info $ "Closing GPIO chip " <> chipPath
+ liftIO $ chipClose chip
+ lineSettingsBracket = allocateEff lineSettingsAlloc lineSettingsDealloc
+ where
+ lineSettingsAlloc = do
+ logMsg Info "Allocating line settings"
+ lineSettings <- liftIO lineSettingsNew
+ logMsg Info $ "With direction set to " <> show output
+ liftIO . void $ lineSettingsSetDirection lineSettings output
+ logMsg Info $ "With output set to " <> show inactive
+ liftIO . void $ lineSettingsSetOutputValue lineSettings inactive
+ return lineSettings
+ lineSettingsDealloc lineSettings = do
+ logMsg Info "Freeing line settings"
+ liftIO $ lineSettingsFree lineSettings
+ lineConfigBracket lineSettings =
+ allocateEff lineConfigAlloc lineConfigDealloc
+ where
+ lineConfigAlloc = do
+ logMsg Info "Allocating line config"
+ logMsg Info $ "With GPIO pins " <> show allPins
+ lineConfig <- liftIO lineConfigNew
+ liftIO . void . unsafeWith (fromList allLines) $ \pinsVector ->
+ lineConfigAddLineSettings
lineConfig
pinsVector
- (CSize $ fromIntegral $ length allPins)
+ (CSize $ fromIntegral pinCount)
lineSettings
- return lineConfig
- lineConfigFree lineConfig = do
- logMsg "Freeing line config"
- G.lineConfigFree lineConfig
- allocateLineConfig lineSettings =
- alloc region (lineConfigNew lineSettings) lineConfigFree
- -- Request config
- requestConfigNew = do
- logMsg "Allocating request config"
- logMsg $ "With consumer " <> consumer
- requestConfig <- G.requestConfigNew
- useAsCString (encodeUtf8 consumer)
- $ G.requestConfigSetConsumer requestConfig
- return requestConfig
- requestConfigFree requestConfig = do
- logMsg "Freeing request config"
- G.requestConfigFree requestConfig
- allocateRequestConfig = alloc region requestConfigNew requestConfigFree
- -- Line request
- requestLines chip requestConfig lineConfig = do
- logMsg "Allocating line request"
- G.requestLines chip requestConfig lineConfig
- lineRequestRelease lineRequest = do
- logMsg "Releasing line request"
- G.lineRequestRelease lineRequest
- allocateLineRequest chip requestConfig lineConfig =
- alloc_
- region
- (requestLines chip requestConfig lineConfig)
- lineRequestRelease
+ return lineConfig
+ lineConfigDealloc lineConfig = do
+ logMsg Info "Freeing line config"
+ liftIO $ lineConfigFree lineConfig
+ requestConfigBracket = allocateEff requestConfigAlloc requestConfigDealloc
+ where
+ requestConfigAlloc = do
+ logMsg Info "Allocating request config"
+ logMsg Info $ "With consumer " <> consumer
+ requestConfig <- liftIO requestConfigNew
+ liftIO . withCString consumer $ requestConfigSetConsumer requestConfig
+ return requestConfig
+ requestConfigDealloc requestConfig = do
+ logMsg Info "Freeing request config"
+ liftIO $ requestConfigFree requestConfig
+ lineRequestBracket chip requestConfig lineConfig =
+ allocateEff lineRequestAlloc lineRequestDealloc
+ where
+ lineRequestAlloc = do
+ logMsg Info "Allocating line request"
+ liftIO $ chipRequestLines chip requestConfig lineConfig
+ lineRequestDealloc lineRequest = do
+ logMsg Info "Releasing line request"
+ liftIO $ lineRequestRelease lineRequest