packages feed

fuyu-gpio-0.1.0.0: src/Fuyu/GPIO/Unsafe.hs

-- |
-- Module      : Fuyu.GPIO.Unsafe
-- Description : Unsafe manual resource allocation and low-level FFI operations.
-- Maintainer  : BassGT
-- Stability   : experimental
-- Portability : POSIX (Linux gpiod v2)
--
-- Manual resource allocation ('openChip', 'closeChip', 'newLineSettings', 'freeLineSettings', etc.)
-- and raw FFI operations for applications that cannot use managed bracket functions.
module Fuyu.GPIO.Unsafe
  ( -- * Types & Security Tokens
    Chip
  , ChipInfo
  , LineInfo
  , Settings
  , Config
  , Request
  , RequestConfig
  , Buffer
  , RawEvent
  , Offset
  , Capacity
  , userBufferCapacity
  , capacity
  , ReadyRequest(..)
  , readyToRequest
  , ReadyChip(..)
  , readyToChip
  , InfoEvent

    -- * Chip Manual Resource Allocation
  , openChip
  , closeChip

    -- * Chip Info Manual Resource Allocation
  , chipInfo
  , freeChipInfo

    -- * Chip Watch / Line Info Event Manual Resource Allocation
  , watchLine
  , unwatchLine
  , readInfoEvent
  , freeInfoEvent

    -- * Line Settings Manual Resource Allocation
  , newLineSettings
  , freeLineSettings

    -- * Line Configuration Manual Resource Allocation
  , newLineConfig
  , freeLineConfig

    -- * Line Request Manual Resource Allocation
  , requestLines
  , releaseLineRequest

    -- * Line Info Manual Resource Allocation & Copy
  , lineInfo
  , freeLineInfo
  , copyLineInfo

    -- * Request Configuration Manual Resource Allocation
  , newRequestConfig
  , freeRequestConfig

    -- * Edge Event Buffer Manual Allocation & Raw Reading
  , newEventBuffer
  , freeEventBuffer
  , readEventsRaw
  ) where

import System.OsPath.Posix (PosixPath)
import qualified Fuyu.GPIO.Direct as D
import Fuyu.GPIO.Exception
import Fuyu.GPIO.Types

--------------------------------------------------------------------------------
-- Chip Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Open a GPIO chip by its filesystem path (e.g. @"\/dev\/gpiochip0"@).
-- Must be manually closed using 'closeChip'.
openChip :: PosixPath -> IO Chip
openChip path = unwrapOrThrow (ChipOpenFailed path) (D.chipOpen path)

-- | Close a GPIO chip handle.
closeChip :: Chip -> IO ()
closeChip = D.chipClose

--------------------------------------------------------------------------------
-- Chip Info Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Retrieve chip info directly.
-- Must be manually freed using 'freeChipInfo'.
chipInfo :: Chip -> IO ChipInfo
chipInfo chip = unwrapOrThrow ChipInfoFailed (D.chipInfo chip)

-- | Free a 'ChipInfo' handle.
freeChipInfo :: ChipInfo -> IO ()
freeChipInfo = D.chipInfoFree

--------------------------------------------------------------------------------
-- Chip Watch / Line Info Event Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Start watching a line for status change events (e.g. requested, released, reconfigured).
--
-- Returns the initial 'LineInfo' snapshot of the line at the moment watching starts.
--
-- * __Memory Management__: The returned 'LineInfo' is allocated in C heap memory and
--   must be manually freed using 'freeLineInfo'.
-- * __Kernel Watch State__: Freeing the 'LineInfo' snapshot does /not/ stop the kernel watch;
--   the kernel continues watching the line until 'unwatchLine' is explicitly called.
--
-- @
-- monitorApp :: IO ()
-- monitorApp = do
--   chip <- openChip "\/dev\/gpiochip0"
--
--   -- Start watching line in the kernel and get initial snapshot
--   info <- watchLine chip (Offset 271)
--
--   -- Inspect initial metadata and free the snapshot immediately
--   dir <- Monitor.direction info
--   freeLineInfo info
--
--   -- ... wait for events with 'Monitor.waitEvent' and read with 'readInfoEvent' ...
--
--   -- Stop watching the line when finished
--   unwatchLine chip (Offset 271)
--   closeChip chip
-- @
watchLine :: Chip -> Offset -> IO LineInfo
watchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipWatchLineInfo chip offset')

-- | Stop watching a line for status change events.
-- Disables kernel notifications previously initiated with 'watchLine'.
unwatchLine :: Chip -> Offset -> IO ()
unwatchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipUnwatchLineInfo chip offset')

-- | Read a line info event from a chip after 'Fuyu.GPIO.Monitor.waitEvent' confirms it is ready.
-- Must be manually freed using 'freeInfoEvent'.
readInfoEvent :: ReadyChip -> IO InfoEvent
readInfoEvent (ReadyChip chip) = unwrapOrThrow ReadInfoEventFailed (D.chipReadInfoEvent chip)

-- | Free an info event object.
freeInfoEvent :: InfoEvent -> IO ()
freeInfoEvent = D.infoEventFree

--------------------------------------------------------------------------------
-- Line Settings Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Allocate a new line settings object.
-- Must be manually freed with 'freeLineSettings'.
newLineSettings :: IO Settings
newLineSettings = unwrapOrThrow LineSettingsNewFailed D.lineSettingsNew

-- | Free a line settings object.
freeLineSettings :: Settings -> IO ()
freeLineSettings = D.lineSettingsFree

--------------------------------------------------------------------------------
-- Line Configuration Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Allocate a new line configuration object.
-- Must be manually freed with 'freeLineConfig'.
newLineConfig :: IO Config
newLineConfig = unwrapOrThrow LineConfigNewFailed D.lineConfigNew

-- | Free a line configuration object.
freeLineConfig :: Config -> IO ()
freeLineConfig = D.lineConfigFree

--------------------------------------------------------------------------------
-- Line Request Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Request GPIO lines from a chip.
-- Must be manually released with 'releaseLineRequest'.
requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request
requestLines chip maybeReqConf lineConf =
  unwrapOrThrow LineRequestFailed (D.chipRequestLines chip maybeReqConf lineConf)

-- | Release a line request handle.
releaseLineRequest :: Request -> IO ()
releaseLineRequest = D.lineRequestRelease

--------------------------------------------------------------------------------
-- Line Info Manual Resource Allocation & Copy
--------------------------------------------------------------------------------

-- | Retrieve information about a specific line on a chip.
-- Must be manually freed using 'freeLineInfo'.
lineInfo :: Chip -> Offset -> IO LineInfo
lineInfo chip offset' = unwrapOrThrow LineInfoFailed (D.chipLineInfo chip offset')

-- | Free a 'LineInfo' handle.
freeLineInfo :: LineInfo -> IO ()
freeLineInfo = D.lineInfoFree

-- | Make a copy of a 'LineInfo' snapshot.
-- Must be manually freed using 'freeLineInfo'.
copyLineInfo :: LineInfo -> IO LineInfo
copyLineInfo info = unwrapOrThrow LineInfoCopyFailed (D.lineInfoCopy info)

--------------------------------------------------------------------------------
-- Request Configuration Manual Resource Allocation
--------------------------------------------------------------------------------

-- | Allocate a new request configuration object.
-- Must be manually freed with 'freeRequestConfig'.
newRequestConfig :: IO RequestConfig
newRequestConfig = unwrapOrThrow RequestConfigNewFailed D.requestConfigNew

-- | Free a request configuration object.
freeRequestConfig :: RequestConfig -> IO ()
freeRequestConfig = D.requestConfigFree

--------------------------------------------------------------------------------
-- Edge Event Buffer Manual Allocation & Raw Reading
--------------------------------------------------------------------------------

-- | Allocate an edge event buffer of the specified capacity.
-- Must be manually freed with 'freeEventBuffer'.
newEventBuffer :: Capacity -> IO Buffer
newEventBuffer cap = unwrapOrThrow EventBufferNewFailed (D.eventBufferNew (capacity cap))

-- | Free an edge event buffer object.
freeEventBuffer :: Buffer -> IO ()
freeEventBuffer = D.eventBufferFree

-- | Read raw edge events into the buffer and return the number of events read.
-- Automatically uses the buffer's full capacity.
-- Throws 'ReadEdgeEventsFailed' on error.
readEventsRaw :: ReadyRequest -> Buffer -> IO Int
readEventsRaw (ReadyRequest req) buf = do
  cap <- D.eventBufferCapacity buf
  unwrapOrThrow ReadEdgeEventsFailed (D.lineRequestReadEdgeEvents req buf cap)