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)