aboutsummaryrefslogtreecommitdiff
path: root/hsm-gpio/Hsm/GPIO.hs
blob: dd691222d7706136d72843e5a6390ad544fad1af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
{-# LANGUAGE OverloadedStrings #-}

module Hsm.GPIO
  ( G.active
  , G.inactive
  , G.LineRequest
  , GPIO(..)
  , setPins
  , setAllPins
  , allocateGPIO
  ) 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 Foreign.Ptr (Ptr)
import Hsm.GPIO.Lib qualified as G
import Hsm.Log qualified as L

logMsg :: Text -> IO ()
logMsg = L.logMsg ["gpio"]

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 = [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

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

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
  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
              lineConfig
              pinsVector
              (CSize $ fromIntegral $ length allPins)
              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