fuyu-gpio-0.1.0.0: src/Fuyu/GPIO/Monitor.hs
{-# LANGUAGE PatternSynonyms #-}
-- |
-- Module : Fuyu.GPIO.Monitor
-- Description : Read-only metadata query functions for ChipInfo and LineInfo,
-- and operations for watching GPIO line status events.
-- Maintainer : BassGT
-- Stability : experimental
-- Portability : POSIX (Linux gpiod v2)
--
-- This module unifies metadata query functions for 'ChipInfo' and 'LineInfo'
-- snapshots, as well as real-time line status change event monitoring ('InfoEvent').
--
-- It is designed to be imported qualified:
--
-- @
-- import qualified Fuyu.GPIO.Monitor as Monitor
-- @
module Fuyu.GPIO.Monitor
( -- * Chip Metadata Snapshot & Inspection
ChipInfo
, withChipInfo
, chipName
, label
, numLines
-- * Line Metadata Snapshot & Inspection
, LineInfo
, withLineInfo
, offset
, lineName
, isUsed
, consumer
, direction
, edgeDetection
, bias
, drive
, isActiveLow
, isDebounced
, debouncePeriod
, eventClock
-- * Line Status Watching & Events
, ReadyChip(..)
, readyToChip
, WaitResult(..)
, Event
, EventType
, pattern Requested
, pattern Released
, pattern ConfigChanged
, withWatchLine
, watchLine
, unwatchLine
, waitEvent
, withEvent
, eventType
, timestampNs
, lineInfo
-- * Types & Patterns
, Chip
, Offset
, pattern Offset
, Direction
, pattern DirAsIs
, pattern DirInput
, pattern DirOutput
, Edge
, pattern EdgeNone
, pattern EdgeRising
, pattern EdgeFalling
, pattern EdgeBoth
, Bias
, pattern BiasAsIs
, pattern BiasUnknown
, pattern BiasDisabled
, pattern BiasPullUp
, pattern BiasPullDown
, Drive
, pattern PushPull
, pattern OpenDrain
, pattern OpenSource
, Clock
, pattern Monotonic
, pattern Realtime
, pattern Hardware
, Timeout
, pattern Nanoseconds
, pattern Immediate
, pattern Infinite
, Timestamp
) where
import Control.Exception (bracket, throwIO)
import Foreign.C.Error (Errno(..))
import qualified Data.ByteString.Char8 as C8
import qualified Fuyu.GPIO.Direct as D
import qualified Fuyu.GPIO.Unsafe as Unsafe
import Fuyu.GPIO.Exception
import Fuyu.GPIO.Types hiding (eventType)
--------------------------------------------------------------------------------
-- Domain Type Aliases
--------------------------------------------------------------------------------
-- | Type alias for 'InfoEvent' designed for qualified use (e.g. @Monitor.Event@).
type Event = InfoEvent
-- | Type alias for 'InfoEventType' designed for qualified use (e.g. @Monitor.EventType@).
type EventType = InfoEventType
--------------------------------------------------------------------------------
-- Chip Metadata Snapshot & Inspection
--------------------------------------------------------------------------------
-- | Retrieve information about a GPIO chip and free it automatically afterwards.
withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
withChipInfo chip = bracket (Unsafe.chipInfo chip) Unsafe.freeChipInfo
-- | Get the name of the GPIO chip (e.g. "gpiochip0").
chipName :: ChipInfo -> IO String
chipName info = C8.unpack <$> D.chipInfoName info
-- | Get the label of the GPIO chip.
label :: ChipInfo -> IO String
label info = C8.unpack <$> D.chipInfoLabel info
-- | Get the total number of lines exposed by the GPIO chip.
numLines :: ChipInfo -> IO Word
numLines = D.chipInfoNumLines
--------------------------------------------------------------------------------
-- Line Metadata Snapshot & Inspection
--------------------------------------------------------------------------------
-- | Retrieve information about a specific line on a chip and free it automatically afterwards.
withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
withLineInfo chip offset' = bracket (Unsafe.lineInfo chip offset') Unsafe.freeLineInfo
-- | Get the numeric 'Offset' of the line from a 'LineInfo' snapshot.
offset :: LineInfo -> IO Offset
offset = D.lineInfoOffset
-- | Get the name of the line (e.g. "GPIO17"), if set.
lineName :: LineInfo -> IO (Maybe String)
lineName info = fmap C8.unpack <$> D.lineInfoName info
-- | Check if the line is currently in use by a consumer kernel driver or user process.
isUsed :: LineInfo -> IO Bool
isUsed = D.lineInfoIsUsed
-- | Get the consumer name string of the line, if in use.
consumer :: LineInfo -> IO (Maybe String)
consumer info = fmap C8.unpack <$> D.lineInfoConsumer info
-- | Get the configured direction of the line ('DirInput', 'DirOutput', 'DirAsIs').
direction :: LineInfo -> IO Direction
direction = D.lineInfoDirection
-- | Get the configured edge detection of the line ('EdgeNone', 'EdgeRising', 'EdgeFalling', 'EdgeBoth').
edgeDetection :: LineInfo -> IO Edge
edgeDetection = D.lineInfoEdgeDetection
-- | Get the configured electrical bias ('BiasDisabled', 'BiasPullUp', 'BiasPullDown', etc.).
bias :: LineInfo -> IO Bias
bias = D.lineInfoBias
-- | Get the configured drive mode ('PushPull', 'OpenDrain', 'OpenSource').
drive :: LineInfo -> IO Drive
drive = D.lineInfoDrive
-- | Check if active-low logic is configured for the line.
isActiveLow :: LineInfo -> IO Bool
isActiveLow = D.lineInfoIsActiveLow
-- | Check if hardware debounce is configured for the line.
isDebounced :: LineInfo -> IO Bool
isDebounced = D.lineInfoIsDebounced
-- | Get the debounce period in microseconds for the line.
debouncePeriod :: LineInfo -> IO Word
debouncePeriod = D.lineInfoDebouncePeriod
-- | Get the event clock source configured for the line ('Monotonic', 'Realtime', 'Hardware').
eventClock :: LineInfo -> IO Clock
eventClock = D.lineInfoEventClock
--------------------------------------------------------------------------------
-- Line Status Watching & Events
--------------------------------------------------------------------------------
-- | Start watching a line for status change events (e.g. requested, released, reconfigured)
-- within a bracket, automatically unwatching the line and freeing the initial snapshot when finished.
-- Passes the initial 'LineInfo' snapshot of the line to the callback.
withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
withWatchLine chip offset' =
bracket
(watchLine chip offset')
(\info -> do
Unsafe.freeLineInfo info
unwatchLine chip offset')
-- | Start watching a line for status change events (e.g. requested, released, reconfigured).
-- Returns the initial 'LineInfo' snapshot of the line.
watchLine :: Chip -> Offset -> IO LineInfo
watchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipWatchLineInfo chip offset')
-- | Stop watching a line for status change events.
unwatchLine :: Chip -> Offset -> IO ()
unwatchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipUnwatchLineInfo chip offset')
-- | Wait for status change info events on any of the watched lines on the chip until the specified timeout.
-- Throws 'WaitInfoEventFailed' on error.
waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
waitEvent chip timeout = do
res <- D.chipWaitInfoEvent chip timeout
case res of
Left (Errno 4) -> waitEvent chip timeout -- Retry on EINTR so GHC RTS can deliver UserInterrupt.
Left err -> throwIO (WaitInfoEventFailed err)
Right D.EventReady -> pure (EventReady (ReadyChip chip))
Right D.Timeout -> pure TimeoutResult
-- | Read a status change info event from a chip once 'waitEvent' indicates it is ready,
-- and automatically free it afterwards.
withEvent :: ReadyChip -> (Event -> IO a) -> IO a
withEvent readyChip = bracket (Unsafe.readInfoEvent readyChip) Unsafe.freeInfoEvent
-- | Get the event type of an 'InfoEvent' ('Requested', 'Released', 'ConfigChanged').
eventType :: Event -> IO EventType
eventType = D.infoEventType
-- | Get the timestamp in nanoseconds of an 'InfoEvent'.
timestampNs :: Event -> IO Timestamp
timestampNs = D.infoEventTimestamp
-- | Get the line info snapshot associated with an 'InfoEvent'.
lineInfo :: Event -> IO LineInfo
lineInfo = D.infoEventLineInfo