packages feed

fuyu-gpio 0.0.9.0 → 0.1.0.0

raw patch · 28 files changed

+1286/−1268 lines, 28 filesdep +filepathdep +manageddep ~fuyu-gpio-directnew-component:exe:03-monitornew-component:exe:04-led-and-buttonPVP ok

version bump matches the API change (PVP)

Dependencies added: filepath, managed

Dependency ranges changed: fuyu-gpio-direct

API changes (from Hackage documentation)

- Fuyu.GPIO: ChipInfoFailed :: Errno -> GpioException
- Fuyu.GPIO: ChipOpenFailed :: FilePath -> Errno -> GpioException
- Fuyu.GPIO: CustomGpioError :: String -> Errno -> GpioException
- Fuyu.GPIO: EventBufferNewFailed :: Errno -> GpioException
- Fuyu.GPIO: EventReady :: !a -> WaitResult a
- Fuyu.GPIO: InvalidArgument :: String -> GpioException
- Fuyu.GPIO: LineConfigNewFailed :: Errno -> GpioException
- Fuyu.GPIO: LineInfoCopyFailed :: Errno -> GpioException
- Fuyu.GPIO: LineInfoFailed :: Errno -> GpioException
- Fuyu.GPIO: LineReconfigureFailed :: Errno -> GpioException
- Fuyu.GPIO: LineRequestFailed :: Errno -> GpioException
- Fuyu.GPIO: LineSettingsNewFailed :: Errno -> GpioException
- Fuyu.GPIO: LineSettingsSetFailed :: Errno -> GpioException
- Fuyu.GPIO: LineValueReadFailed :: Errno -> GpioException
- Fuyu.GPIO: LineValueWriteFailed :: Errno -> GpioException
- Fuyu.GPIO: RawEdgeEventCopyFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadEdgeEventsFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadInfoEventFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO: RequestConfigNewFailed :: Errno -> GpioException
- Fuyu.GPIO: TimeoutResult :: WaitResult a
- Fuyu.GPIO: WaitEdgeEventsFailed :: Errno -> GpioException
- Fuyu.GPIO: WaitInfoEventFailed :: Errno -> GpioException
- Fuyu.GPIO: data GpioException
- Fuyu.GPIO: data WaitResult a
- Fuyu.GPIO: lineInfo :: InfoEvent -> IO LineInfo
- Fuyu.GPIO: newtype ReadyChip
- Fuyu.GPIO: offsetFromName :: Chip -> ByteString -> IO Offset
- Fuyu.GPIO: path :: Chip -> IO ByteString
- Fuyu.GPIO: pattern ConfigChanged :: InfoEventType
- Fuyu.GPIO: pattern Immediate :: Timeout
- Fuyu.GPIO: pattern Infinite :: Timeout
- Fuyu.GPIO: pattern Nanoseconds :: CULong -> Timeout
- Fuyu.GPIO: pattern Released :: InfoEventType
- Fuyu.GPIO: pattern Requested :: InfoEventType
- Fuyu.GPIO: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO: setValue :: Request -> Offset -> Value -> IO ()
- Fuyu.GPIO: setValues :: Request -> Vector Value -> IO ()
- Fuyu.GPIO: setValuesSubset :: Request -> Vector Offset -> Vector Value -> IO ()
- Fuyu.GPIO: settings :: Config -> Offset -> IO Settings
- Fuyu.GPIO: type InfoEvent = InfoEvent
- Fuyu.GPIO: type InfoEventType = InfoEventType
- Fuyu.GPIO: type LineInfo = LineInfo
- Fuyu.GPIO: type Timeout = TimeoutNs
- Fuyu.GPIO: type Timestamp = TimestampNs
- Fuyu.GPIO: unwatchLine :: Chip -> Offset -> IO ()
- Fuyu.GPIO: value :: Request -> Offset -> IO Value
- Fuyu.GPIO: values :: Request -> IO (Vector Value)
- Fuyu.GPIO: valuesSubset :: Request -> Vector Offset -> IO (Vector Value)
- Fuyu.GPIO: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
- Fuyu.GPIO: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO: withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a
- Fuyu.GPIO: withGpioApp :: IO a -> IO ()
- Fuyu.GPIO: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip: fd :: Chip -> IO Fd
- Fuyu.GPIO.Chip: gpiodAPIVersion :: IO ByteString
- Fuyu.GPIO.Chip: isGPIOChip :: FilePath -> IO Bool
- Fuyu.GPIO.Chip: offsetFromName :: Chip -> ByteString -> IO Offset
- Fuyu.GPIO.Chip: path :: Chip -> IO ByteString
- Fuyu.GPIO.Chip: withChip :: FilePath -> (Chip -> IO a) -> IO a
- Fuyu.GPIO.Chip: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Info: label :: ChipInfo -> IO ByteString
- Fuyu.GPIO.Chip.Info: name :: ChipInfo -> IO ByteString
- Fuyu.GPIO.Chip.Info: numLines :: ChipInfo -> IO Word
- Fuyu.GPIO.Chip.Info: type ChipInfo = ChipInfo
- Fuyu.GPIO.Chip.Info: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Info.Unsafe: chipInfo :: Chip -> IO ChipInfo
- Fuyu.GPIO.Chip.Info.Unsafe: freeChipInfo :: ChipInfo -> IO ()
- Fuyu.GPIO.Chip.Info.Unsafe: type ChipInfo = ChipInfo
- Fuyu.GPIO.Chip.Unsafe: closeChip :: Chip -> IO ()
- Fuyu.GPIO.Chip.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
- Fuyu.GPIO.Chip.Unsafe: openChip :: FilePath -> IO Chip
- Fuyu.GPIO.Chip.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
- Fuyu.GPIO.Chip.Unsafe: type Chip = Chip
- Fuyu.GPIO.Chip.Unsafe: type InfoEvent = InfoEvent
- Fuyu.GPIO.Chip.Watch: EventReady :: !a -> WaitResult a
- Fuyu.GPIO.Chip.Watch: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO.Chip.Watch: TimeoutResult :: WaitResult a
- Fuyu.GPIO.Chip.Watch: data WaitResult a
- Fuyu.GPIO.Chip.Watch: eventType :: InfoEvent -> IO InfoEventType
- Fuyu.GPIO.Chip.Watch: lineInfo :: InfoEvent -> IO LineInfo
- Fuyu.GPIO.Chip.Watch: newtype ReadyChip
- Fuyu.GPIO.Chip.Watch: pattern ConfigChanged :: InfoEventType
- Fuyu.GPIO.Chip.Watch: pattern Immediate :: Timeout
- Fuyu.GPIO.Chip.Watch: pattern Infinite :: Timeout
- Fuyu.GPIO.Chip.Watch: pattern Nanoseconds :: CULong -> Timeout
- Fuyu.GPIO.Chip.Watch: pattern Offset :: CUInt -> Offset
- Fuyu.GPIO.Chip.Watch: pattern Released :: InfoEventType
- Fuyu.GPIO.Chip.Watch: pattern Requested :: InfoEventType
- Fuyu.GPIO.Chip.Watch: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO.Chip.Watch: timestampNs :: InfoEvent -> IO Timestamp
- Fuyu.GPIO.Chip.Watch: type Chip = Chip
- Fuyu.GPIO.Chip.Watch: type InfoEvent = InfoEvent
- Fuyu.GPIO.Chip.Watch: type InfoEventType = InfoEventType
- Fuyu.GPIO.Chip.Watch: type LineInfo = LineInfo
- Fuyu.GPIO.Chip.Watch: type Offset = LineOffset
- Fuyu.GPIO.Chip.Watch: type Timeout = TimeoutNs
- Fuyu.GPIO.Chip.Watch: type Timestamp = TimestampNs
- Fuyu.GPIO.Chip.Watch: unwatchLine :: Chip -> Offset -> IO ()
- Fuyu.GPIO.Chip.Watch: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
- Fuyu.GPIO.Chip.Watch: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO.Chip.Watch: withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a
- Fuyu.GPIO.Chip.Watch: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Watch.Unsafe: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO.Chip.Watch.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
- Fuyu.GPIO.Chip.Watch.Unsafe: newtype ReadyChip
- Fuyu.GPIO.Chip.Watch.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
- Fuyu.GPIO.Chip.Watch.Unsafe: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO.Chip.Watch.Unsafe: type Chip = Chip
- Fuyu.GPIO.Chip.Watch.Unsafe: type InfoEvent = InfoEvent
- Fuyu.GPIO.EdgeEvent: bufferEvent :: Buffer -> Word -> IO Event
- Fuyu.GPIO.EdgeEvent: parseEvent :: Event -> IO EdgeEvent
- Fuyu.GPIO.EdgeEvent: withRawEvents :: ReadyRequest -> Buffer -> (Event -> IO a) -> IO (NonEmpty a)
- Fuyu.GPIO.EdgeEvent.Unsafe: ReadyRequest :: Request -> ReadyRequest
- Fuyu.GPIO.EdgeEvent.Unsafe: capacity :: Capacity -> Word
- Fuyu.GPIO.EdgeEvent.Unsafe: data Capacity
- Fuyu.GPIO.EdgeEvent.Unsafe: freeEventBuffer :: Buffer -> IO ()
- Fuyu.GPIO.EdgeEvent.Unsafe: newEventBuffer :: Capacity -> IO Buffer
- Fuyu.GPIO.EdgeEvent.Unsafe: newtype ReadyRequest
- Fuyu.GPIO.EdgeEvent.Unsafe: readEventsRaw :: ReadyRequest -> Buffer -> IO Int
- Fuyu.GPIO.EdgeEvent.Unsafe: type Buffer = EventBuffer
- Fuyu.GPIO.EdgeEvent.Unsafe: userBufferCapacity :: Word -> Capacity
- Fuyu.GPIO.Exception: instance GHC.Exception.Type.Exception Fuyu.GPIO.Exception.GpioException
- Fuyu.GPIO.Exception: instance GHC.Show.Show Fuyu.GPIO.Exception.GpioException
- Fuyu.GPIO.Exception: withGpioApp :: IO a -> IO ()
- Fuyu.GPIO.Line: activeLow :: Settings -> IO Bool
- Fuyu.GPIO.Line: addSettings :: Config -> Vector Offset -> Settings -> IO ()
- Fuyu.GPIO.Line: bias :: Settings -> IO Bias
- Fuyu.GPIO.Line: chipName :: Request -> IO ByteString
- Fuyu.GPIO.Line: configuredOffsets :: Config -> IO (Vector Offset)
- Fuyu.GPIO.Line: debouncePeriodUs :: Settings -> IO Word
- Fuyu.GPIO.Line: direction :: Settings -> IO Direction
- Fuyu.GPIO.Line: drive :: Settings -> IO Drive
- Fuyu.GPIO.Line: edgeDetection :: Settings -> IO Edge
- Fuyu.GPIO.Line: eventClock :: Settings -> IO Clock
- Fuyu.GPIO.Line: fd :: Request -> IO Fd
- Fuyu.GPIO.Line: numLines :: Request -> IO Word
- Fuyu.GPIO.Line: numOffsets :: Config -> IO Word
- Fuyu.GPIO.Line: outputValue :: Settings -> IO Value
- Fuyu.GPIO.Line: pattern Active :: Value
- Fuyu.GPIO.Line: pattern BiasAsIs :: Bias
- Fuyu.GPIO.Line: pattern BiasDisabled :: Bias
- Fuyu.GPIO.Line: pattern BiasPullDown :: Bias
- Fuyu.GPIO.Line: pattern BiasPullUp :: Bias
- Fuyu.GPIO.Line: pattern BiasUnknown :: Bias
- Fuyu.GPIO.Line: pattern DirAsIs :: Direction
- Fuyu.GPIO.Line: pattern DirInput :: Direction
- Fuyu.GPIO.Line: pattern DirOutput :: Direction
- Fuyu.GPIO.Line: pattern EdgeBoth :: Edge
- Fuyu.GPIO.Line: pattern EdgeFalling :: Edge
- Fuyu.GPIO.Line: pattern EdgeNone :: Edge
- Fuyu.GPIO.Line: pattern EdgeRising :: Edge
- Fuyu.GPIO.Line: pattern Hardware :: Clock
- Fuyu.GPIO.Line: pattern Inactive :: Value
- Fuyu.GPIO.Line: pattern Monotonic :: Clock
- Fuyu.GPIO.Line: pattern Offset :: CUInt -> Offset
- Fuyu.GPIO.Line: pattern OpenDrain :: Drive
- Fuyu.GPIO.Line: pattern OpenSource :: Drive
- Fuyu.GPIO.Line: pattern PushPull :: Drive
- Fuyu.GPIO.Line: pattern Realtime :: Clock
- Fuyu.GPIO.Line: pattern ValueError :: Value
- Fuyu.GPIO.Line: reconfigureLines :: Request -> Config -> IO ()
- Fuyu.GPIO.Line: requestedOffsets :: Request -> IO (Vector Offset)
- Fuyu.GPIO.Line: resetConfig :: Config -> IO ()
- Fuyu.GPIO.Line: resetSettings :: Settings -> IO ()
- Fuyu.GPIO.Line: setActiveLow :: Settings -> Bool -> IO ()
- Fuyu.GPIO.Line: setBias :: Settings -> Bias -> IO ()
- Fuyu.GPIO.Line: setDebouncePeriodUs :: Settings -> Word -> IO ()
- Fuyu.GPIO.Line: setDirection :: Settings -> Direction -> IO ()
- Fuyu.GPIO.Line: setDrive :: Settings -> Drive -> IO ()
- Fuyu.GPIO.Line: setEdgeDetection :: Settings -> Edge -> IO ()
- Fuyu.GPIO.Line: setEventClock :: Settings -> Clock -> IO ()
- Fuyu.GPIO.Line: setOutputValue :: Settings -> Value -> IO ()
- Fuyu.GPIO.Line: setOutputValues :: Config -> Vector Value -> IO ()
- Fuyu.GPIO.Line: setValue :: Request -> Offset -> Value -> IO ()
- Fuyu.GPIO.Line: setValues :: Request -> Vector Value -> IO ()
- Fuyu.GPIO.Line: setValuesSubset :: Request -> Vector Offset -> Vector Value -> IO ()
- Fuyu.GPIO.Line: settings :: Config -> Offset -> IO Settings
- Fuyu.GPIO.Line: type Bias = LineBias
- Fuyu.GPIO.Line: type Clock = LineClock
- Fuyu.GPIO.Line: type Config = LineConfig
- Fuyu.GPIO.Line: type Direction = LineDirection
- Fuyu.GPIO.Line: type Drive = LineDrive
- Fuyu.GPIO.Line: type Edge = LineEdge
- Fuyu.GPIO.Line: type Offset = LineOffset
- Fuyu.GPIO.Line: type Request = LineRequest
- Fuyu.GPIO.Line: type RequestConfig = RequestConfig
- Fuyu.GPIO.Line: type Settings = LineSettings
- Fuyu.GPIO.Line: type Value = LineValue
- Fuyu.GPIO.Line: value :: Request -> Offset -> IO Value
- Fuyu.GPIO.Line: values :: Request -> IO (Vector Value)
- Fuyu.GPIO.Line: valuesSubset :: Request -> Vector Offset -> IO (Vector Value)
- Fuyu.GPIO.Line: withConfig :: (Config -> IO a) -> IO a
- Fuyu.GPIO.Line: withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a
- Fuyu.GPIO.Line: withSettings :: (Settings -> IO a) -> IO a
- Fuyu.GPIO.Line.Info: bias :: LineInfo -> IO Bias
- Fuyu.GPIO.Line.Info: consumer :: LineInfo -> IO (Maybe ByteString)
- Fuyu.GPIO.Line.Info: debouncePeriod :: LineInfo -> IO Word
- Fuyu.GPIO.Line.Info: direction :: LineInfo -> IO Direction
- Fuyu.GPIO.Line.Info: drive :: LineInfo -> IO Drive
- Fuyu.GPIO.Line.Info: edgeDetection :: LineInfo -> IO Edge
- Fuyu.GPIO.Line.Info: eventClock :: LineInfo -> IO Clock
- Fuyu.GPIO.Line.Info: isActiveLow :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: isDebounced :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: isUsed :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: name :: LineInfo -> IO (Maybe ByteString)
- Fuyu.GPIO.Line.Info: offset :: LineInfo -> IO Offset
- Fuyu.GPIO.Line.Info: type LineInfo = LineInfo
- Fuyu.GPIO.Line.Info: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Line.Info.Unsafe: copyLineInfo :: LineInfo -> IO LineInfo
- Fuyu.GPIO.Line.Info.Unsafe: freeLineInfo :: LineInfo -> IO ()
- Fuyu.GPIO.Line.Info.Unsafe: lineInfo :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO.Line.Info.Unsafe: type LineInfo = LineInfo
- Fuyu.GPIO.Line.Unsafe: freeConfig :: Config -> IO ()
- Fuyu.GPIO.Line.Unsafe: freeSettings :: Settings -> IO ()
- Fuyu.GPIO.Line.Unsafe: newConfig :: IO Config
- Fuyu.GPIO.Line.Unsafe: newSettings :: IO Settings
- Fuyu.GPIO.Line.Unsafe: releaseRequest :: Request -> IO ()
- Fuyu.GPIO.Line.Unsafe: requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request
- Fuyu.GPIO.Line.Unsafe: type Chip = Chip
- Fuyu.GPIO.Line.Unsafe: type Config = LineConfig
- Fuyu.GPIO.Line.Unsafe: type Request = LineRequest
- Fuyu.GPIO.Line.Unsafe: type RequestConfig = RequestConfig
- Fuyu.GPIO.Line.Unsafe: type Settings = LineSettings
- Fuyu.GPIO.RequestConfig.Unsafe: freeRequestConfig :: RequestConfig -> IO ()
- Fuyu.GPIO.RequestConfig.Unsafe: newRequestConfig :: IO RequestConfig
- Fuyu.GPIO.RequestConfig.Unsafe: type RequestConfig = RequestConfig
+ Fuyu.GPIO: chipFd :: Chip -> IO Fd
+ Fuyu.GPIO: chipPath :: Chip -> IO FilePath
+ Fuyu.GPIO: lineFd :: Request -> IO Fd
+ Fuyu.GPIO: lineOffsetFromName :: Chip -> String -> IO Offset
+ Fuyu.GPIO: lineSettings :: Config -> Offset -> IO Settings
+ Fuyu.GPIO: lineValue :: Request -> Offset -> IO Value
+ Fuyu.GPIO: lineValues :: Request -> IO [Value]
+ Fuyu.GPIO: lineValuesSubset :: Request -> [Offset] -> IO (Vector Value)
+ Fuyu.GPIO: setLineValue :: Request -> Offset -> Value -> IO ()
+ Fuyu.GPIO: setLineValues :: Request -> [Value] -> IO ()
+ Fuyu.GPIO: setLineValuesSubset :: Request -> [Offset] -> [Value] -> IO ()
+ Fuyu.GPIO.EdgeEvent: bufferRawEvent :: Buffer -> Word -> IO (Maybe RawEvent)
+ Fuyu.GPIO.EdgeEvent: forRawEvents :: ReadyRequest -> Buffer -> (RawEvent -> IO a) -> IO (NonEmpty a)
+ Fuyu.GPIO.EdgeEvent: forRawEvents_ :: ReadyRequest -> Buffer -> (RawEvent -> IO b) -> IO ()
+ Fuyu.GPIO.EdgeEvent: parseRawEvent :: RawEvent -> IO Event
+ Fuyu.GPIO.EdgeEvent: type EventType = EdgeEventType
+ Fuyu.GPIO.EdgeEvent: type RawEvent = RawEdgeEvent
+ Fuyu.GPIO.Exception: instance GHC.Internal.Exception.Type.Exception Fuyu.GPIO.Exception.GpioException
+ Fuyu.GPIO.Exception: instance GHC.Internal.Show.Show Fuyu.GPIO.Exception.GpioException
+ Fuyu.GPIO.Monitor: EventReady :: !a -> WaitResult a
+ Fuyu.GPIO.Monitor: ReadyChip :: Chip -> ReadyChip
+ Fuyu.GPIO.Monitor: TimeoutResult :: WaitResult a
+ Fuyu.GPIO.Monitor: bias :: LineInfo -> IO Bias
+ Fuyu.GPIO.Monitor: chipName :: ChipInfo -> IO String
+ Fuyu.GPIO.Monitor: consumer :: LineInfo -> IO (Maybe String)
+ Fuyu.GPIO.Monitor: data WaitResult a
+ Fuyu.GPIO.Monitor: debouncePeriod :: LineInfo -> IO Word
+ Fuyu.GPIO.Monitor: direction :: LineInfo -> IO Direction
+ Fuyu.GPIO.Monitor: drive :: LineInfo -> IO Drive
+ Fuyu.GPIO.Monitor: edgeDetection :: LineInfo -> IO Edge
+ Fuyu.GPIO.Monitor: eventClock :: LineInfo -> IO Clock
+ Fuyu.GPIO.Monitor: eventType :: Event -> IO EventType
+ Fuyu.GPIO.Monitor: isActiveLow :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: isDebounced :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: isUsed :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: label :: ChipInfo -> IO String
+ Fuyu.GPIO.Monitor: lineInfo :: Event -> IO LineInfo
+ Fuyu.GPIO.Monitor: lineName :: LineInfo -> IO (Maybe String)
+ Fuyu.GPIO.Monitor: newtype ReadyChip
+ Fuyu.GPIO.Monitor: numLines :: ChipInfo -> IO Word
+ Fuyu.GPIO.Monitor: offset :: LineInfo -> IO Offset
+ Fuyu.GPIO.Monitor: pattern BiasAsIs :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasDisabled :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasPullDown :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasPullUp :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasUnknown :: Bias
+ Fuyu.GPIO.Monitor: pattern ConfigChanged :: InfoEventType
+ Fuyu.GPIO.Monitor: pattern DirAsIs :: Direction
+ Fuyu.GPIO.Monitor: pattern DirInput :: Direction
+ Fuyu.GPIO.Monitor: pattern DirOutput :: Direction
+ Fuyu.GPIO.Monitor: pattern EdgeBoth :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeFalling :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeNone :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeRising :: Edge
+ Fuyu.GPIO.Monitor: pattern Hardware :: Clock
+ Fuyu.GPIO.Monitor: pattern Immediate :: Timeout
+ Fuyu.GPIO.Monitor: pattern Infinite :: Timeout
+ Fuyu.GPIO.Monitor: pattern Monotonic :: Clock
+ Fuyu.GPIO.Monitor: pattern Nanoseconds :: CULong -> Timeout
+ Fuyu.GPIO.Monitor: pattern Offset :: CUInt -> Offset
+ Fuyu.GPIO.Monitor: pattern OpenDrain :: Drive
+ Fuyu.GPIO.Monitor: pattern OpenSource :: Drive
+ Fuyu.GPIO.Monitor: pattern PushPull :: Drive
+ Fuyu.GPIO.Monitor: pattern Realtime :: Clock
+ Fuyu.GPIO.Monitor: pattern Released :: InfoEventType
+ Fuyu.GPIO.Monitor: pattern Requested :: InfoEventType
+ Fuyu.GPIO.Monitor: readyToChip :: ReadyChip -> Chip
+ Fuyu.GPIO.Monitor: timestampNs :: Event -> IO Timestamp
+ Fuyu.GPIO.Monitor: type Bias = LineBias
+ Fuyu.GPIO.Monitor: type Chip = Chip
+ Fuyu.GPIO.Monitor: type ChipInfo = ChipInfo
+ Fuyu.GPIO.Monitor: type Clock = LineClock
+ Fuyu.GPIO.Monitor: type Direction = LineDirection
+ Fuyu.GPIO.Monitor: type Drive = LineDrive
+ Fuyu.GPIO.Monitor: type Edge = LineEdge
+ Fuyu.GPIO.Monitor: type Event = InfoEvent
+ Fuyu.GPIO.Monitor: type EventType = InfoEventType
+ Fuyu.GPIO.Monitor: type LineInfo = LineInfo
+ Fuyu.GPIO.Monitor: type Offset = LineOffset
+ Fuyu.GPIO.Monitor: type Timeout = TimeoutNs
+ Fuyu.GPIO.Monitor: type Timestamp = TimestampNs
+ Fuyu.GPIO.Monitor: unwatchLine :: Chip -> Offset -> IO ()
+ Fuyu.GPIO.Monitor: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
+ Fuyu.GPIO.Monitor: watchLine :: Chip -> Offset -> IO LineInfo
+ Fuyu.GPIO.Monitor: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withEvent :: ReadyChip -> (Event -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
+ Fuyu.GPIO.Unsafe: ReadyChip :: Chip -> ReadyChip
+ Fuyu.GPIO.Unsafe: ReadyRequest :: Request -> ReadyRequest
+ Fuyu.GPIO.Unsafe: capacity :: Capacity -> Word
+ Fuyu.GPIO.Unsafe: chipInfo :: Chip -> IO ChipInfo
+ Fuyu.GPIO.Unsafe: closeChip :: Chip -> IO ()
+ Fuyu.GPIO.Unsafe: copyLineInfo :: LineInfo -> IO LineInfo
+ Fuyu.GPIO.Unsafe: data Capacity
+ Fuyu.GPIO.Unsafe: freeChipInfo :: ChipInfo -> IO ()
+ Fuyu.GPIO.Unsafe: freeEventBuffer :: Buffer -> IO ()
+ Fuyu.GPIO.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineConfig :: Config -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineInfo :: LineInfo -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineSettings :: Settings -> IO ()
+ Fuyu.GPIO.Unsafe: freeRequestConfig :: RequestConfig -> IO ()
+ Fuyu.GPIO.Unsafe: lineInfo :: Chip -> Offset -> IO LineInfo
+ Fuyu.GPIO.Unsafe: newEventBuffer :: Capacity -> IO Buffer
+ Fuyu.GPIO.Unsafe: newLineConfig :: IO Config
+ Fuyu.GPIO.Unsafe: newLineSettings :: IO Settings
+ Fuyu.GPIO.Unsafe: newRequestConfig :: IO RequestConfig
+ Fuyu.GPIO.Unsafe: newtype ReadyChip
+ Fuyu.GPIO.Unsafe: newtype ReadyRequest
+ Fuyu.GPIO.Unsafe: openChip :: PosixPath -> IO Chip
+ Fuyu.GPIO.Unsafe: readEventsRaw :: ReadyRequest -> Buffer -> IO Int
+ Fuyu.GPIO.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
+ Fuyu.GPIO.Unsafe: readyToChip :: ReadyChip -> Chip
+ Fuyu.GPIO.Unsafe: readyToRequest :: ReadyRequest -> Request
+ Fuyu.GPIO.Unsafe: releaseLineRequest :: Request -> IO ()
+ Fuyu.GPIO.Unsafe: requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request
+ Fuyu.GPIO.Unsafe: type Buffer = EventBuffer
+ Fuyu.GPIO.Unsafe: type Chip = Chip
+ Fuyu.GPIO.Unsafe: type ChipInfo = ChipInfo
+ Fuyu.GPIO.Unsafe: type Config = LineConfig
+ Fuyu.GPIO.Unsafe: type InfoEvent = InfoEvent
+ Fuyu.GPIO.Unsafe: type LineInfo = LineInfo
+ Fuyu.GPIO.Unsafe: type Offset = LineOffset
+ Fuyu.GPIO.Unsafe: type RawEvent = RawEdgeEvent
+ Fuyu.GPIO.Unsafe: type Request = LineRequest
+ Fuyu.GPIO.Unsafe: type RequestConfig = RequestConfig
+ Fuyu.GPIO.Unsafe: type Settings = LineSettings
+ Fuyu.GPIO.Unsafe: unwatchLine :: Chip -> Offset -> IO ()
+ Fuyu.GPIO.Unsafe: userBufferCapacity :: Word -> Capacity
+ Fuyu.GPIO.Unsafe: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO: addSettings :: Config -> Vector Offset -> Settings -> IO ()
+ Fuyu.GPIO: addSettings :: Config -> [Offset] -> Settings -> IO ()
- Fuyu.GPIO: chipName :: Request -> IO ByteString
+ Fuyu.GPIO: chipName :: Request -> IO String
- Fuyu.GPIO: configuredOffsets :: Config -> IO (Vector Offset)
+ Fuyu.GPIO: configuredOffsets :: Config -> IO [Offset]
- Fuyu.GPIO: gpiodAPIVersion :: IO ByteString
+ Fuyu.GPIO: gpiodAPIVersion :: IO String
- Fuyu.GPIO: requestedOffsets :: Request -> IO (Vector Offset)
+ Fuyu.GPIO: requestedOffsets :: Request -> IO [Offset]
- Fuyu.GPIO: setOutputValues :: Config -> Vector Value -> IO ()
+ Fuyu.GPIO: setOutputValues :: Config -> [Value] -> IO ()
- Fuyu.GPIO.EdgeEvent: copyEvent :: Event -> IO Event
+ Fuyu.GPIO.EdgeEvent: copyEvent :: RawEvent -> IO RawEvent
- Fuyu.GPIO.EdgeEvent: eventType :: Event -> IO EdgeEventType
+ Fuyu.GPIO.EdgeEvent: eventType :: RawEvent -> IO EventType
- Fuyu.GPIO.EdgeEvent: globalSeqNo :: Event -> IO Word64
+ Fuyu.GPIO.EdgeEvent: globalSeqNo :: RawEvent -> IO Word64
- Fuyu.GPIO.EdgeEvent: lineOffset :: Event -> IO Offset
+ Fuyu.GPIO.EdgeEvent: lineOffset :: RawEvent -> IO Offset
- Fuyu.GPIO.EdgeEvent: lineSeqNo :: Event -> IO Offset
+ Fuyu.GPIO.EdgeEvent: lineSeqNo :: RawEvent -> IO Offset
- Fuyu.GPIO.EdgeEvent: readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty EdgeEvent)
+ Fuyu.GPIO.EdgeEvent: readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty Event)
- Fuyu.GPIO.EdgeEvent: timestampNs :: Event -> IO Timestamp
+ Fuyu.GPIO.EdgeEvent: timestampNs :: RawEvent -> IO Timestamp
- Fuyu.GPIO.EdgeEvent: type Event = RawEdgeEvent
+ Fuyu.GPIO.EdgeEvent: type Event = EdgeEvent
- Fuyu.GPIO.Exception: ChipOpenFailed :: FilePath -> Errno -> GpioException
+ Fuyu.GPIO.Exception: ChipOpenFailed :: PosixPath -> Errno -> GpioException

Files

CHANGELOG.md view
@@ -1,5 +1,20 @@ # Revision history for fuyu-gpio +## 0.1.0.0 -- 2026-09-06++* **API Simplification & Architectural Consolidation**:+  * Merged `Fuyu.GPIO.Chip` and `Fuyu.GPIO.Line` into a unified `Fuyu.GPIO` core module.+  * Merged `Chip.Info`, `Line.Info`, and `Chip.Watch` into `Fuyu.GPIO.Monitor`.+  * Consolidated all manual and low-level FFI operations into a single `Fuyu.GPIO.Unsafe` module.+  * Explicit naming for unsafe allocators (`newLineSettings`, `freeLineSettings`, `newLineConfig`, `freeLineConfig`, `releaseLineRequest`) to eliminate domain ambiguity.+* **Dependencies & Compatibility**:+  * Updated dependency on `fuyu-gpio-direct` to `^>= 0.2.0.0`.+  * Added upper bounds for `filepath (< 1.6)` and `managed (< 1.1)` compliant with PVP.+* **Documentation & Examples**:+  * Reached 100% Haddock documentation coverage across all library modules.+  * Added structured progressive examples (01 to 05) with `Control.Monad.Managed` and `transformers` (`ContT` / `StateT`).+  * Updated README with detailed module architecture and design concepts.+ ## 0.0.9.0 -- 2026-08-13  * Initial release. High-level managed abstraction layer for libgpiod built on top of fuyu-gpio-direct.
examples/01-blink.hs view
@@ -3,47 +3,48 @@ -- using nested resource allocation brackets ('withChip', 'withSettings', 'withConfig', 'withRequest'). module Main where --- High-level resource brackets & utility functions-import Fuyu.GPIO.Chip (withChip, isGPIOChip)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line+-- High-level resource brackets & utility functions.+-- This import libgpiod Core API functions for chip and line (definitions, settings, configuration, request).+-- Also this module contains the miscellaneous functions for libgpiod ('isGPIOChip' & 'gpiodAPIVersion')+import Fuyu.GPIO --- Base & third-party libraries+-- Base & third-party libraries.+-- Usually we are going to use these modules and their functions. import Control.Concurrent (threadDelay) import Control.Monad (replicateM_)-import Data.Vector.Storable (singleton) --- We could check if this file is a GPIO Chip with 'isGPIOChip' function-chipPath :: FilePath-chipPath = "/dev/gpiochip0" +-- We could check if this file is a GPIO Chip with 'isGPIOChip' function.+myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"   -- In Orange Pi devices we could find this information with 'gpio readall' command (or in docs) -- In this case the line offset 269 corresponds to physical pin 7 -ledOffset :: Line.Offset -ledOffset = Line.Offset 269 +ledOffset :: Offset +ledOffset = Offset 269  + main :: IO () main = do-   isChip <- isGPIOChip chipPath+   isChip <- isGPIOChip myChipPath    if isChip      then do         putStrLn "Example started: LED blinking"        runApp-     else putStrLn $ chipPath ++ " does not correspond to a valid GPIO Chip"+     else putStrLn $ myChipPath ++ " does not correspond to a valid GPIO Chip"  runApp :: IO () runApp = do-   withChip chipPath $ \chip -> do+   withChip myChipPath $ \chip -> do       withSettings $ \settings -> do-         Line.setDirection settings Line.DirOutput+         setDirection settings DirOutput          withConfig $ \config -> do-            Line.addSettings config (singleton ledOffset) settings+            addSettings config [ledOffset] settings             -- The use of Nothing instead of a 'RequestConfig' means that we are using a NULL request configuration object.             -- (Do not confuse Fuyu.GPIO.RequestConfig with Fuyu.GPIO.Line.Config, the first one is used for kernel options             -- and the second one is used for line config).               withRequest chip Nothing config $ \request -> do                replicateM_ 10 $ do -                 Line.setValue request ledOffset Line.Active+                 setLineValue request ledOffset Active                  threadDelay 500000 -- 0.5 seconds pause   -                 Line.setValue request ledOffset Line.Inactive+                 setLineValue request ledOffset Inactive                  threadDelay 500000 -- 0.5 seconds pause
examples/02-button.hs view
@@ -3,64 +3,64 @@ -- using a user-space event buffer with 'userBufferCapacity 1' to read button press events. module Main where --- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import Fuyu.GPIO.Exception (withGpioApp)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line-import Fuyu.GPIO.EdgeEvent (withBuffer)-import qualified Fuyu.GPIO.EdgeEvent as Event+-- High-level resource brackets & exception handling.+-- I recommend a qualified import of 'Fuyu.GPIO.EdgeEvent', for example as Edge.+import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge  --- Base & third-party libraries+-- Base & third-party libraries. +-- To have a clean output when we press Ctrl+c. +import Control.Exception (catch, throwIO, AsyncException(UserInterrupt)) import Control.Monad (forever)-import Data.Vector.Storable (singleton) -chipPath :: FilePath-chipPath = "/dev/gpiochip0" +myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"   -- This constant defines the maximum duration 'waitEvents' will wait for an event. -- (This timeout can also be configured as infinite or immediate).-fiveSecondsNs :: Event.Timeout-fiveSecondsNs = Event.Nanoseconds 5000000000 +fiveSecondsNs :: Edge.Timeout+fiveSecondsNs = Edge.Nanoseconds 5000000000   -- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer. -- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity' -- (passing 0 defaults to 64).-bufferCapacity :: Event.Capacity-bufferCapacity = Event.userBufferCapacity 1 +myBufferCapacity :: Edge.Capacity+myBufferCapacity = Edge.userBufferCapacity 1   -buttonOffset :: Line.Offset-buttonOffset = Line.Offset 257 +buttonOffset :: Offset+buttonOffset = Offset 257   -buttonSettings :: Line.Settings -> IO ()+buttonSettings :: Settings -> IO () buttonSettings stgs = do-  Line.setDirection stgs Line.DirInput     -- Configure line as input mode-  Line.setBias stgs Line.BiasPullUp        -- Enable internal pull-up resistor-                                           -- (the physical button connects GND when pressed, driving the line to Inactive)-  Line.setDebouncePeriodUs stgs 20000      -- 20ms debounce period to filter out mechanical contact bounce without threadDelay-  Line.setEdgeDetection stgs Line.EdgeBoth -- Listen for both Rising and Falling edge transitions+  setDirection stgs DirInput     -- Configure line as input mode+  setBias stgs BiasPullUp        -- Enable internal pull-up resistor+                                 -- (the physical button connects GND when pressed, driving the line to Inactive)+  setDebouncePeriodUs stgs 20000 -- 20ms debounce period to filter out mechanical contact bounce without threadDelay+  setEdgeDetection stgs EdgeBoth -- Listen for both Rising and Falling edge transitions -buttonWorker :: Line.Request -> Event.Buffer -> IO ()+buttonWorker :: Request -> Edge.Buffer -> IO () buttonWorker req buf = do-  res <- Event.waitEvents req fiveSecondsNs+  res <- Edge.waitEvents req fiveSecondsNs   case res of -    Event.EventReady readyReq -> do-      events <- Event.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1)+    Edge.EventReady readyReq -> do+      events <- Edge.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1)       print events    -    Event.TimeoutResult -> putStrLn "Timeout: No event was read" -- Printed after the 5-second wait timeout expires+    Edge.TimeoutResult -> putStrLn "Timeout: No event was read" -- Printed after the 5-second wait timeout expires  main :: IO ()-main = withGpioApp runApp+main = runApp `catch` \exc -> case exc of+  UserInterrupt -> putStrLn "\nLoop terminated successfully!"+  other         -> throwIO other  runApp :: IO () runApp = do-  withChip chipPath $ \chip -> do+  withChip myChipPath $ \chip -> do     withSettings $ \settings -> do       buttonSettings settings       withConfig $ \config -> do-        Line.addSettings config (singleton buttonOffset) settings+        addSettings config [buttonOffset] settings         withRequest chip Nothing config $ \request -> do-          withBuffer bufferCapacity $ \buffer -> do+          Edge.withBuffer myBufferCapacity $ \buffer -> do             putStrLn "Loop started: Press the button to generate events or Ctrl+C to exit"             forever (buttonWorker request buffer)
− examples/03-led-and-button.hs
@@ -1,112 +0,0 @@--- In this example we will learn how to coordinate GPIO output (LED blinking) and input (button press)--- concurrently. We launch two worker threads with 'forkIO' and use an 'MVar' to dynamically--- control the LED blinking speed upon detecting button press edge events.-module Main where---- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import Fuyu.GPIO.Exception (withGpioApp)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line-import Fuyu.GPIO.EdgeEvent (withBuffer)-import qualified Fuyu.GPIO.EdgeEvent as Event---- Base & third-party libraries-import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_, newMVar, readMVar, threadDelay)-import Control.Exception (finally)-import Control.Monad (forever)-import Data.Vector.Storable (singleton)-import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)--chipPath :: FilePath-chipPath = "/dev/gpiochip0"---- This constant defines the maximum duration 'waitEvents' will wait for an event.--- A short 100ms timeout yields execution back to the RTS so worker threads run smoothly.-waitTimeoutNs :: Event.Timeout-waitTimeoutNs = Event.Nanoseconds 100000000 ---- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer.--- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity'--- (passing 0 defaults to 64).-bufferCapacity :: Event.Capacity-bufferCapacity = Event.userBufferCapacity 1--ledOffset :: Line.Offset-ledOffset = Line.Offset 256 --buttonOffset :: Line.Offset-buttonOffset = Line.Offset 271--type Microseconds = Int---- Available blinking speed states-data LooptimeState = OneSec | HalfSec | FifthOfSec | TenthOfSec-  deriving (Eq, Show)---- Convert LooptimeState into delay duration in microseconds-stateToMicroseconds :: LooptimeState -> Microseconds-stateToMicroseconds OneSec     = 1000000 -- 1.0s delay-stateToMicroseconds HalfSec    = 500000  -- 0.5s delay-stateToMicroseconds FifthOfSec = 200000  -- 0.2s delay-stateToMicroseconds TenthOfSec = 100000  -- 0.1s delay---- Cycle to the next blinking speed state-nextSpeed :: LooptimeState -> LooptimeState -nextSpeed OneSec     = HalfSec-nextSpeed HalfSec    = FifthOfSec-nextSpeed FifthOfSec = TenthOfSec-nextSpeed TenthOfSec = OneSec--ledSettings :: Line.Settings -> IO ()-ledSettings stgs = Line.setDirection stgs Line.DirOutput--buttonSettings :: Line.Settings -> IO ()-buttonSettings stgs = do-  Line.setDirection stgs Line.DirInput     -- Configure line as input mode-  Line.setBias stgs Line.BiasPullUp        -- Enable internal pull-up resistor-                                           -- (the physical button connects GND when pressed, driving the line to Inactive)-  Line.setDebouncePeriodUs stgs 80000      -- 80ms native kernel debounce period to filter out mechanical contact bounce without threadDelay-  Line.setEdgeDetection stgs Line.EdgeFalling -- Listen for Falling edge transitions (button press to GND)---- Blinks the LED continuously using the delay duration read from the MVar-ledWorker :: Line.Request -> MVar LooptimeState -> IO ()-ledWorker req speedMVar = forever $ do-  lts <- readMVar speedMVar-  let delayUs = stateToMicroseconds lts-  Line.setValue req ledOffset Line.Active-  threadDelay delayUs-  Line.setValue req ledOffset Line.Inactive-  threadDelay delayUs---- Listens for button edge events and cycles the blinking speed state-buttonWorker :: Line.Request -> Event.Buffer -> MVar LooptimeState -> IO ()-buttonWorker req buf speedMVar = do-  res <- Event.waitEvents req waitTimeoutNs-  case res of-    Event.EventReady readyReq -> do-      _events <- Event.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1)-      modifyMVar_ speedMVar (return . nextSpeed)-    Event.TimeoutResult -> threadDelay 20000 -- 20ms pause to yield file descriptor to LED worker thread--main :: IO ()-main = withGpioApp runApp--runApp :: IO ()-runApp = do-  hSetBuffering stdout NoBuffering-  initialSpeedMVar <- newMVar OneSec-  withChip chipPath $ \chip -> do  -    withSettings $ \buttonStgs -> do-      buttonSettings buttonStgs-      withSettings $ \ledStgs -> do -        ledSettings ledStgs-        withConfig $ \config -> do-          Line.addSettings config (singleton ledOffset) ledStgs-          Line.addSettings config (singleton buttonOffset) buttonStgs-          withRequest chip Nothing config $ \request -> do-            withBuffer bufferCapacity $ \buffer -> do-              Line.setValue request ledOffset Line.Inactive-              putStrLn "Loop started: LED blinking concurrently. Press the button to change speed, or Ctrl+C to exit"-              tid <- forkIO (forever $ ledWorker request initialSpeedMVar)-              forever (buttonWorker request buffer initialSpeedMVar) `finally` killThread tid
+ examples/03-monitor.hs view
@@ -0,0 +1,71 @@+-- In this example we will learn how to monitor GPIO line status changes across processes using chip watching.+-- This example structures the application into modular helper blocks ('runApp', 'monitorApp', 'lineApp'),+-- serving as an intermediate modular phase before introducing resource management with 'Control.Monad.Managed'+-- in the next example (04-led-and-button.hs) to eliminate deeply nested brackets.+module Main where++-- High-level resource brackets & exception handling.+import Fuyu.GPIO++-- Qualified Domain Modules.+-- Same as Fuyu.GPIO.EdgeEvent I sugest an qualified import. +import qualified Fuyu.GPIO.Monitor as Monitor  ++-- Base & third-party libraries.+import Control.Concurrent (forkIO, threadDelay)++myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"++targetOffset :: Offset+targetOffset = Offset 257++waitTimeoutNs :: Monitor.Timeout+waitTimeoutNs = Monitor.Nanoseconds 5000000000 -- 5 seconds++main :: IO ()+main = do+  putStrLn "Starting line status event monitor..."+  runApp +  putStrLn "Line status event monitor completed successfully."++-- We could use some modular application blocks as the next ones to avoid+-- "super nested" functions. Personally I sugest to use this kind of organization+-- or use managed package (next example) for medium size programs.++-- 'runApp' centralizes worker threads and watchers organized in structured blocks.+runApp :: IO ()+runApp = do+  putStrLn "Opening GPIO chip and starting line status watching..."+  withChip myChipPath $ \chip -> do+    -- Register the line watch in the kernel before any interaction+    -- (required so the kernel starts queueing status events for targetOffset)+    Monitor.withWatchLine chip targetOffset $ \_lineInfo -> do+      _ <- forkIO $ lineApp chip+      monitorApp chip ++-- 'monitorApp' waits for status change events using 'waitEvent' and security token 'ReadyChip'+monitorApp :: Chip -> IO ()+monitorApp chip = do     +  putStrLn "Waiting for line status change event (timeout: 5s)..."+  res <- Monitor.waitEvent chip waitTimeoutNs+  case res of+    -- Same pattern as 'Fuyu.GPIO.EdgeEvent.waitEvents'+    Monitor.EventReady readyChip -> do+      Monitor.withEvent readyChip $ \infoEvent -> do+        -- In this case, we expect a 'Requested' info event type+        eventType <- Monitor.eventType infoEvent      +        putStrLn ("Event received! " ++ show eventType)+    Monitor.TimeoutResult -> putStrLn "Wait timed out (timeout)."++-- 'lineApp' simulates line interactions (requesting access to targetOffset) in a concurrent thread+lineApp :: Chip -> IO () +lineApp chip = do   +  withSettings $ \settings -> do+    setDirection settings DirAsIs  +    withConfig $ \config -> do+      addSettings config [targetOffset] settings +      withRequest chip Nothing config $ \request -> do+        name <- chipName request+        putStrLn ("Line request created successfully on chip: " ++ show name)+        threadDelay 500000 -- Hold requested line briefly
+ examples/04-led-and-button.hs view
@@ -0,0 +1,159 @@+-- In this example we will learn how to coordinate GPIO output (LED blinking) and input (button press)+-- concurrently using 'forkIO' and an 'MVar' to dynamically control the LED blinking speed upon+-- detecting button press edge events.+--+-- We introduce 'Control.Monad.Managed' ('managed', 'runManaged') to acquire and compose nested resources+-- in a clean, linear 'do' block. This effectively eliminates the "Pyramid of Doom" (deeply nested 'with*'+-- brackets) in an accessible, lightweight manner before introducing more advanced abstractions like+-- monad transformers ('ContT' / 'StateT') in example 05.+module Main where++-- High-level resource brackets & exception handling.+import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge ++-- Base & third-party libraries.+import Control.Monad.Managed (managed, runManaged, liftIO)+-- For concurrent medium size programs, 'Control.Concurrent' is awesome.  +import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_, newMVar, readMVar, threadDelay)+-- Besides the functions and types we used before (to manage Ctrl+c exit), we alse need 'finally' to ensure+-- the cleanup of the concurrent thread when the main loop ends or is interrupted.+import Control.Exception (finally, catch, throwIO, AsyncException(UserInterrupt))+import Control.Monad (forever)+import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)++myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"++-- This constant defines the maximum duration 'waitEvents' will wait for an event.+-- A short 100ms timeout yields execution back to the RTS so worker threads run smoothly.+waitTimeoutNs :: Edge.Timeout+waitTimeoutNs = Edge.Nanoseconds 100000000 ++-- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer.+-- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity'+-- (passing 0 defaults to 64).+bufferCapacity :: Edge.Capacity+bufferCapacity = Edge.userBufferCapacity 1++ledOffset :: Offset+ledOffset = Offset 256 ++buttonOffset :: Offset+buttonOffset = Offset 271++type Microseconds = Int++-- Available blinking speed states.+data LooptimeState = OneSec | HalfSec | FifthOfSec | TenthOfSec+  deriving (Eq, Show)++-- Convert LooptimeState into delay duration in microseconds.+stateToMicroseconds :: LooptimeState -> Microseconds+stateToMicroseconds OneSec     = 1000000 -- 1.0s delay+stateToMicroseconds HalfSec    = 500000  -- 0.5s delay+stateToMicroseconds FifthOfSec = 200000  -- 0.2s delay+stateToMicroseconds TenthOfSec = 100000  -- 0.1s delay++-- Cycle to the next blinking speed state.+nextSpeed :: LooptimeState -> LooptimeState +nextSpeed OneSec     = HalfSec+nextSpeed HalfSec    = FifthOfSec+nextSpeed FifthOfSec = TenthOfSec+nextSpeed TenthOfSec = OneSec++myLedSettings :: Settings -> IO ()+myLedSettings stgs = setDirection stgs DirOutput++myButtonSettings :: Settings -> IO ()+myButtonSettings stgs = do+  setDirection stgs DirInput        -- Configure line as input mode.+  setBias stgs BiasPullUp           -- Enable internal pull-up resistor.+                                    -- (the physical button connects GND when pressed, driving the line to Inactive).+  setDebouncePeriodUs stgs 80000    -- 80ms native kernel debounce period to filter out mechanical contact bounce without threadDelay.+  setEdgeDetection stgs EdgeFalling -- Listen for Falling edge transitions (button press to GND).++-- Blinks the LED continuously using the delay duration read from the MVar.+ledWorker :: Request -> MVar LooptimeState -> IO ()+ledWorker req speedMVar = forever $ do+  lts <- readMVar speedMVar+  let delayUs = stateToMicroseconds lts+  setLineValue req ledOffset Active+  threadDelay delayUs+  setLineValue req ledOffset Inactive+  threadDelay delayUs++-- Listens for button edge events and cycles the blinking speed state.+buttonWorker :: Request -> Edge.Buffer -> MVar LooptimeState -> IO ()+buttonWorker req buf speedMVar = do+  res <- Edge.waitEvents req waitTimeoutNs+  case res of+    Edge.EventReady readyReq -> do+      _events <- Edge.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1).+      modifyMVar_ speedMVar (return . nextSpeed)+    Edge.TimeoutResult -> threadDelay 20000 -- 20ms pause to yield file descriptor to LED worker thread.++withAppConfig :: Settings -> Settings -> (Config -> IO r) -> IO r+withAppConfig ledStgs btnStgs action =+   withConfig $ \config -> do   +     addSettings config [ledOffset]  ledStgs+     addSettings config [buttonOffset]  btnStgs+     action config+        +withAppRequest :: Chip -> Config -> (Request -> IO r) -> IO r+withAppRequest chip = withRequest chip Nothing ++main :: IO ()+main = runApp `catch` \exc -> case exc of+  UserInterrupt -> putStrLn "\nLoop terminated seccessfully!"+  other         -> throwIO other ++--  The next lines shows us how to manage libgpiod resources with managed package +-- (avoiding the Pyramid of Doom).+  +runApp :: IO ()+runApp = do+  hSetBuffering stdout NoBuffering+  initialSpeedMVar <- newMVar OneSec++  -- Instead of nesting 6 levels of 'with*' brackets, 'runManaged' flattens+  -- resource acquisition sequentially while guaranteeing safe cleanup on exit.+  runManaged $ do+    chip        <- managed (withChip myChipPath)+    ledSettings <- managed withSettings+    btnSettings <- managed withSettings+    liftIO $ do+      myLedSettings ledSettings+      myButtonSettings btnSettings+    config      <- managed (withAppConfig ledSettings btnSettings)+    request     <- managed (withAppRequest chip config)+    buffer      <- managed (Edge.withBuffer bufferCapacity)+    liftIO (appLoop request initialSpeedMVar buffer)+  +appLoop :: Request -> MVar LooptimeState -> Edge.Buffer -> IO ()+appLoop request speed buffer = do+  setLineValue request ledOffset Inactive+  putStrLn "Loop started: LED blinking concurrently. Press the button to change speed, or Ctrl+C to exit"+  tid <- forkIO (forever $ ledWorker request speed)+  forever (buttonWorker request buffer speed) `finally` killThread tid++{-+-- For comparison, here is how 'runApp' would look without 'Control.Monad.Managed'+-- (demonstrating the "Pyramid of Doom" caused by multiple nested brackets):++runAppPyramid :: IO ()+runAppPyramid = do+  hSetBuffering stdout NoBuffering+  initialSpeedMVar <- newMVar OneSec+  withChip chipPath $ \chip -> do  +    withSettings $ \btnStgs -> do+      myButtonSettings btnStgs+      withSettings $ \ledStgs -> do +        myLedSettings ledStgs+        withConfig $ \config -> do+          addSettings config (singleton ledOffset) ledStgs+          addSettings config (singleton buttonOffset) btnStgs+          withRequest chip Nothing config $ \request -> do+            withBuffer bufferCapacity $ \buffer -> do+              appLoop request initialSpeedMVar buffer+-}
− examples/04-line-watch.hs
@@ -1,69 +0,0 @@--- In this example we will learn how to monitor GPIO line status changes across processes using chip watching.--- This example structures the application into modular helper functions ('runApp', 'monitorApp', 'lineApp'),--- serving as an intermediate modular phase before refactoring nested brackets with monadic continuation ('ContT').-module Main where---- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (Chip, withChip)-import Fuyu.GPIO.Line (withRequest, withConfig, withSettings)-import Fuyu.GPIO.Exception (withGpioApp)---- Qualified Domain Modules-import qualified Fuyu.GPIO.Chip.Watch as Watch-import qualified Fuyu.GPIO.Line as Line---- Base & third-party libraries-import Control.Concurrent (forkIO, threadDelay)-import Data.Vector.Storable (singleton)--chipPath :: FilePath-chipPath = "/dev/gpiochip0"--targetOffset :: Line.Offset-targetOffset = Line.Offset 257--waitTimeoutNs :: Watch.Timeout-waitTimeoutNs = Watch.Nanoseconds 5000000000 -- 5 seconds--main :: IO ()-main = withGpioApp $ do-  putStrLn "Starting line status event monitor..."-  runApp -  putStrLn "Line status event monitor completed successfully."---- 'runApp' centralizes worker threads and watchers to avoid pyramid of doom-runApp :: IO ()-runApp = do-  putStrLn "Opening GPIO chip and starting line status watching..."-  withChip chipPath $ \chip -> do-    -- Register the line watch in the kernel before any interaction-    -- (required so the kernel starts queueing status events for targetOffset)-    Watch.withWatchLine chip targetOffset $ \_lineInfo -> do-      _ <- forkIO $ lineApp chip-      monitorApp chip ---- 'monitorApp' waits for status change events using 'waitEvent' and security token 'ReadyChip'-monitorApp :: Chip -> IO ()-monitorApp chip = do     -  putStrLn "Waiting for line status change event (timeout: 5s)..."-  res <- Watch.waitEvent chip waitTimeoutNs-  case res of-    -- Same pattern as 'Fuyu.GPIO.EdgeEvent.waitEvents'-    Watch.EventReady readyChip -> do-      Watch.withEvent readyChip $ \infoEvent -> do-        -- In this case, we expect a 'Requested' info event type-        eventType <- Watch.eventType infoEvent      -        putStrLn ("Event received! " ++ show eventType)-    Watch.TimeoutResult -> putStrLn "Wait timed out (timeout)."---- 'lineApp' simulates line interactions (requesting access to targetOffset) in a concurrent thread-lineApp :: Chip -> IO () -lineApp chip = do   -  withSettings $ \settings -> do-    Line.setDirection settings Line.DirAsIs  -    withConfig $ \config -> do-      Line.addSettings config (singleton targetOffset) settings -      withRequest chip Nothing config $ \request -> do-        name <- Line.chipName request-        putStrLn ("Line request created successfully on chip: " ++ show name)-        threadDelay 500000 -- Hold requested line briefly
examples/05-request-config.hs view
@@ -5,42 +5,42 @@ module Main where  -- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import qualified Fuyu.GPIO.Line as Line-import qualified Fuyu.GPIO.EdgeEvent as EdgeEvent  +import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge  import qualified Fuyu.GPIO.RequestConfig as ReqConf  -import Fuyu.GPIO.Exception (withGpioApp) --- Base & third-party libraries+-- Base & core libraries ('transformers' is a core GHC boot package with zero external dependencies). import Control.Monad.Trans.Cont (evalContT, ContT(..))+-- We import 'Control.Monad.Trans.State.Strict' directly from 'transformers' rather than+-- 'Control.Monad.State' from 'mtl' to keep external dependencies minimal.+-- We explicitly choose the strict variant ('.Strict') and use 'modify'' to force evaluation of state+-- updates eagerly. This prevents space leaks (accumulation of unevaluated thunks in memory)+-- as encoder tick counts change continuously. import Control.Monad.Trans.State.Strict (StateT, evalStateT, gets, modify') import Control.Monad.IO.Class (liftIO) import Control.Monad (forever, when)-import qualified Data.Vector.Storable as V (fromList)+-- To have a clean exit when Ctrl+C is pressed.+import Control.Exception (catch, throwIO, AsyncException(UserInterrupt)) import Data.List.NonEmpty (NonEmpty(..)) -chipPath :: FilePath-chipPath = "/dev/gpiochip0"+myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"  -- Line offsets for the rotary encoder signals-offsetCLK :: Line.Offset-offsetCLK = Line.Offset 256+offsetCLK :: Offset+offsetCLK = Offset 256 -offsetDT :: Line.Offset-offsetDT = Line.Offset 271+offsetDT :: Offset+offsetDT = Offset 271  -- Timeout for waiting on edge events (5 seconds)-fiveSecondsNs :: EdgeEvent.Timeout-fiveSecondsNs = EdgeEvent.Nanoseconds 5000000000+fiveSecondsNs :: Edge.Timeout+fiveSecondsNs = Edge.Nanoseconds 5000000000  -- Setting user buffer capacity to 1 guarantees that 'readEvents' returns exactly 1 event at a time. -- This simplifies pattern matching to '(ev :| _)' without losing any events in the kernel queue.-capacity :: EdgeEvent.Capacity-capacity = EdgeEvent.userBufferCapacity 1------------------------------------------------------------------------------------- Encoder State Definition---------------------------------------------------------------------------------+capacity :: Edge.Capacity+capacity = Edge.userBufferCapacity 1  -- Clean pure Haskell record representing the quadrature state and step count. data EncoderState = EncoderState@@ -54,71 +54,66 @@ initialState = EncoderState { clkPin = 1, dtPin = 1, position = 0 }  main :: IO ()-main = withGpioApp $ do-  putStrLn "Starting request config example..."-  runApp-  putStrLn "Request config example completed successfully."------------------------------------------------------------------------------------- Helper Configurator Brackets---------------------------------------------------------------------------------+main = do +  putStrLn "Starting request config example (Press Ctrl+C to exit)..."+  runApp `catch` \exc -> case exc of+    UserInterrupt -> putStrLn "\nLoop terminated successfully!"+    other         -> throwIO other --- Encapsulates the creation and configuration of RequestConfig (consumer label & buffer size).+-- We need some helpers configurator brackets. +-- This one encapsulates the creation and configuration of RequestConfig (consumer label & buffer size). withAppRequestConfig :: (ReqConf.RequestConfig -> IO r) -> IO r withAppRequestConfig action = ReqConf.withRequestConfig $ \reqconf -> do   ReqConf.setConsumer reqconf "encoder-app"   ReqConf.setBufferSize reqconf 256   action reqconf --- Encapsulates line settings configuration (input mode, 1ms debounce, edge detection).-withAppLineSettings :: (Line.Settings -> IO r) -> IO r-withAppLineSettings action = Line.withSettings $ \settings -> do-  Line.setDirection settings Line.DirInput-  Line.setDebouncePeriodUs settings 1000 -- 1ms debounce suitable for rotary encoder hardware-  Line.setEdgeDetection settings Line.EdgeBoth +-- To encapsulates line settings configuration (input mode, 1ms debounce, edge detection).+withAppLineSettings :: (Settings -> IO r) -> IO r+withAppLineSettings action = withSettings $ \settings -> do+  setDirection settings DirInput+  setDebouncePeriodUs settings 1000 -- 1ms debounce suitable for rotary encoder hardware+  setEdgeDetection settings EdgeBoth    action settings --- Encapsulates building line configuration for target pin offsets (CLK & DT).-withAppLineConfig :: Line.Settings -> (Line.Config -> IO r) -> IO r-withAppLineConfig settings action = Line.withConfig $ \config -> do-  Line.addSettings config (V.fromList [offsetCLK, offsetDT]) settings+-- And to encapsulates building line configuration for target pin offsets (CLK & DT).+withAppLineConfig :: Settings -> (Config -> IO r) -> IO r+withAppLineConfig settings action = withConfig $ \config -> do+  addSettings config [offsetCLK, offsetDT] settings   action config ------------------------------------------------------------------------------------ Resource Setup using ContT and Execution with StateT------------------------------------------------------------------------------------- Monadic resource setup using 'ContT' flattens nested 'with...' brackets into a linear 'do' block.--- 'evalStateT' then runs the application loop with managed pure state ('EncoderState').+-- Monadic resource setup using 'ContT' (from 'transformers') flattens nested 'with...' brackets+-- into a linear 'do' block. This achieves the same goal as 'runManaged' in example 04, but using+-- standard core transformers without external dependencies.+-- 'evalStateT' then runs the stateful application loop initialized with 'initialState'. runApp :: IO () runApp = evalContT $ do-  chip     <- ContT $ withChip chipPath+  chip     <- ContT $ withChip myChipPath   reqconf  <- ContT withAppRequestConfig   settings <- ContT withAppLineSettings   config   <- ContT $ withAppLineConfig settings-  request  <- ContT $ Line.withRequest chip (Just reqconf) config-  buffer   <- ContT $ EdgeEvent.withBuffer capacity+  request  <- ContT $ withRequest chip (Just reqconf) config+  buffer   <- ContT $ Edge.withBuffer capacity -  -- Run stateful application loop starting with 'initialState'+  -- Run stateful application loop starting with 'initialState'.   liftIO $ evalStateT (appLoop request buffer) initialState ------------------------------------------------------------------------------------ Encoder Application Loop using MonadState (StateT)---------------------------------------------------------------------------------- -- Application loop running in 'StateT EncoderState IO ()'.-appLoop :: Line.Request -> EdgeEvent.Buffer -> StateT EncoderState IO ()+-- Unlike example 04 which used an 'MVar' to share mutable state across concurrent threads ('forkIO'),+-- this example is a single-threaded sequential event loop. 'StateT' provides pure, structured state+-- transitions without the synchronization overhead or locking primitives of MVars.+appLoop :: Request -> Edge.Buffer -> StateT EncoderState IO () appLoop request buffer = forever $ do-  result <- liftIO $ EdgeEvent.waitEvents request fiveSecondsNs+  result <- liftIO $ Edge.waitEvents request fiveSecondsNs   case result of-    EdgeEvent.TimeoutResult -> +    Edge.TimeoutResult ->        liftIO $ putStrLn "No edge event was read (timeout)." -    EdgeEvent.EventReady req -> do-      (ev :| _) <- liftIO $ EdgeEvent.readEvents req buffer+    Edge.EventReady req -> do+      (ev :| _) <- liftIO $ Edge.readEvents req buffer       oldPos    <- gets position       -      -- Update pure state cleanly using strict 'modify''+      -- Update pure state cleanly using strict 'modify'.        modify' (updateEncoderState ev)              newPos    <- gets position@@ -129,13 +124,13 @@ -- When CLK transitions to LOW (Falling edge), we inspect the current state of DT: --   - DT == 1 (HIGH) -> Clockwise rotation (+1) --   - DT == 0 (LOW)  -> Counter-Clockwise rotation (-1)-updateEncoderState :: EdgeEvent.EdgeEvent -> EncoderState -> EncoderState-updateEncoderState (EdgeEvent.EdgeEvent offset evType _) st = case (offset, evType) of-  (Line.Offset 256, EdgeEvent.Falling) ->+updateEncoderState :: Edge.Event -> EncoderState -> EncoderState+updateEncoderState (Edge.EdgeEvent offset evType _) st = case (offset, evType) of+  (Offset 256, Edge.Falling) ->     let delta  = if dtPin st == 1 then 1 else (-1)     in st { clkPin = 0, position = position st + delta } -  (Line.Offset 256, EdgeEvent.Rising)  -> st { clkPin = 1 }-  (Line.Offset 271, EdgeEvent.Falling) -> st { dtPin = 0 }-  (Line.Offset 271, EdgeEvent.Rising)  -> st { dtPin = 1 }-  _                                    -> st+  (Offset 256, Edge.Rising)  -> st { clkPin = 1 }+  (Offset 271, Edge.Falling) -> st { dtPin = 0 }+  (Offset 271, Edge.Rising)  -> st { dtPin = 1 }+  _                          -> st
fuyu-gpio.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               fuyu-gpio-version:            0.0.9.0+version:            0.1.0.0 synopsis:           High-level, type-safe interface for Linux GPIO (libgpiod v2). description:        High-level, type-safe, and resource-managed Haskell interface for Linux GPIO character devices using libgpiod v2. Built on top of fuyu-gpio-direct, fuyu-gpio provides automatic memory management (bracket / with* style), typed exception handling, metadata snapshots, and zero-copy vector operations for high-performance GPIO I/O. license:            LGPL-2.1-or-later@@ -22,29 +22,20 @@ library     import:           warnings     exposed-modules:  Fuyu.GPIO,-                      Fuyu.GPIO.Chip,-                      Fuyu.GPIO.Chip.Info,-                      Fuyu.GPIO.Chip.Info.Unsafe,-                      Fuyu.GPIO.Chip.Unsafe,-                      Fuyu.GPIO.Line,-                      Fuyu.GPIO.Line.Info,-                      Fuyu.GPIO.Line.Info.Unsafe,-                      Fuyu.GPIO.Line.Unsafe,+                      Fuyu.GPIO.Unsafe, +                      Fuyu.GPIO.Monitor,                        Fuyu.GPIO.RequestConfig,-                      Fuyu.GPIO.RequestConfig.Unsafe,                       Fuyu.GPIO.EdgeEvent,-                      Fuyu.GPIO.EdgeEvent.Unsafe,-                      Fuyu.GPIO.Chip.Watch,-                      Fuyu.GPIO.Chip.Watch.Unsafe,                       Fuyu.GPIO.Exception      other-modules:    Fuyu.GPIO.Types      -- other-extensions:     build-depends:    base >= 4.18 && < 5,-                      fuyu-gpio-direct ^>=0.1.0.0,+                      fuyu-gpio-direct ^>=0.2.0.0,                       bytestring ^>=0.12.1.0,-                      vector ^>=0.13.1.0+                      vector ^>=0.13.1.0,+                      filepath >= 1.5.5.0 && < 1.6       hs-source-dirs:   src@@ -68,6 +59,7 @@             base >= 4.18 && < 5,             fuyu-gpio,             vector ^>= 0.13.1.0,+            managed >= 1.0.10 && < 1.1,             transformers >= 0.5 && < 0.7,             bytestring ^>= 0.12.1.0 @@ -80,16 +72,16 @@     import:           example-config     main-is:          02-button.hs     -executable 03-led-and-button-    import:           example-config-    main-is:          03-led-and-button.hs-    ghc-options:      -threaded --executable 04-line-watch+executable 03-monitor     import:           example-config-    main-is:          04-line-watch.hs+    main-is:          03-monitor.hs     ghc-options:      -threaded -rtsopts     +executable 04-led-and-button+    import:           example-config+    main-is:          04-led-and-button.hs+    ghc-options:      -threaded + executable 05-request-config       import:           example-config     main-is:          05-request-config.hs
src/Fuyu/GPIO.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | -- Module      : Fuyu.GPIO -- Description : High-level, type-safe Haskell interface for Linux GPIO (libgpiod v2).@@ -5,26 +6,363 @@ -- Stability   : experimental -- Portability : POSIX (Linux GPIO character device interface) ----- This is the main umbrella module for @fuyu-gpio@, providing high-level,--- managed resource wrappers ('withChip', 'withSettings', 'withConfig', 'withRequest')--- and exception handling ('GpioException', 'withGpioApp') for Linux GPIO character devices.+-- This module groups functions related to GPIO chip and line handling from the+-- <https://libgpiod.readthedocs.io/en/master/core_api.html libgpiod Core API>.+-- Specifically, these functions cover GPIO Chip, GPIO line settings,+-- GPIO line configuration, GPIO line requests, and libgpiod miscellaneous interfaces. ----- For detailed metadata inspection, import "Fuyu.GPIO.Chip.Info" or "Fuyu.GPIO.Line.Info" qualified.--- For manual/unmanaged FFI resource lifecycle, import the corresponding @.Unsafe@ submodules. module Fuyu.GPIO-  ( -- * Domain Modules-    module Fuyu.GPIO.Chip-  , module Fuyu.GPIO.Line-  , module Fuyu.GPIO.RequestConfig-  , module Fuyu.GPIO.EdgeEvent+  ( -- * GPIO Chip+    Chip+  , withChip+  , chipPath+  , lineOffsetFromName+  , chipFd -    -- * Exception & App Runner-  , GpioException(..)-  , withGpioApp+    -- * GPIO line definitions+  , 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+  , Value+  , pattern Active+  , pattern Inactive+  , pattern ValueError++    -- * GPIO Line Settings+  , Settings+  , withSettings+  , setDirection+  , direction+  , setEdgeDetection+  , edgeDetection+  , setBias+  , bias+  , setDrive+  , drive+  , setEventClock+  , eventClock+  , setActiveLow+  , activeLow+  , setDebouncePeriodUs+  , debouncePeriodUs+  , setOutputValue+  , outputValue+  , resetSettings++    -- * GPIO Line Config+  , Config+  , withConfig+  , addSettings+  , lineSettings+  , setOutputValues+  , numOffsets+  , configuredOffsets+  , resetConfig++    -- * GPIO Line Request+  , Request+  , RequestConfig+  , withRequest+  , lineValue+  , lineValues+  , lineValuesSubset+  , setLineValue+  , setLineValues+  , setLineValuesSubset+  , chipName+  , numLines+  , requestedOffsets+  , lineFd+  , reconfigureLines++    -- * General Utilities+  , isGPIOChip+  , gpiodAPIVersion   ) where -import Fuyu.GPIO.Chip hiding (fd, eventType, timestampNs)-import Fuyu.GPIO.Line hiding (fd)-import Fuyu.GPIO.RequestConfig-import Fuyu.GPIO.EdgeEvent-import Fuyu.GPIO.Exception (GpioException(..), withGpioApp)+import Control.Exception (bracket, throwIO)+import System.OsPath.Posix (encodeFS, decodeFS)+import qualified Data.ByteString.Char8 as C8+import qualified Data.Vector.Storable as V+import System.Posix.Types (Fd)+import qualified Fuyu.GPIO.Direct as D+import Fuyu.GPIO.Unsafe+  ( openChip+  , closeChip+  , newLineSettings+  , freeLineSettings+  , newLineConfig+  , freeLineConfig+  , requestLines+  , releaseLineRequest+  )+import Fuyu.GPIO.Exception+import Fuyu.GPIO.Types++--------------------------------------------------------------------------------+-- GPIO Chip+--------------------------------------------------------------------------------++-- | Open a GPIO chip by filesystem path (e.g. @"\/dev\/gpiochip0"@) and automatically close it when finished.+--+-- @+-- main :: IO ()+-- main = do+--   withChip "\/dev\/gpiochip0" $ \chip -> do+--     someFunc chip+-- @+withChip :: FilePath -> (Chip -> IO a) -> IO a+withChip str f = do+  path' <- encodeFS str+  bracket (openChip path') closeChip f++-- | Retrieve chip filesystem path as a 'FilePath'.+chipPath :: Chip -> IO FilePath+chipPath chip = do+  path' <- unwrapOrThrow ChipInfoFailed (D.chipPath chip)+  decodeFS path'++-- | Map a GPIO line name (e.g. "GPIO17") to its numeric 'Offset' on the chip.+lineOffsetFromName :: Chip -> String -> IO Offset+lineOffsetFromName chip name =+  unwrapOrThrow LineInfoFailed (D.chipLineOffsetFromName chip (C8.pack name))++-- | Get the underlying Linux file descriptor associated with the GPIO chip handle.+chipFd :: Chip -> IO Fd+chipFd = D.chipFd++--------------------------------------------------------------------------------+-- GPIO Line Settings+--------------------------------------------------------------------------------++-- | Allocate a new line settings object and free it automatically afterwards.+withSettings :: (Settings -> IO a) -> IO a+withSettings = bracket newLineSettings freeLineSettings++-- | Set the line direction in the settings.+setDirection :: Settings -> Direction -> IO ()+setDirection set dir = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDirection set dir)++-- | Get the line direction from the settings.+direction :: Settings -> IO Direction+direction = D.lineSettingsDirection++-- | Set edge detection in the settings.+setEdgeDetection :: Settings -> Edge -> IO ()+setEdgeDetection set edge = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEdgeDetection set edge)++-- | Get edge detection from the settings.+edgeDetection :: Settings -> IO Edge+edgeDetection = D.lineSettingsEdgeDetection++-- | Set electrical bias in the settings.+setBias :: Settings -> Bias -> IO ()+setBias _ BiasUnknown = throwIO $ InvalidArgument "setBias: BiasUnknown is a read-only state and cannot be set as a bias configuration."+setBias set biasVal   = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetBias set biasVal)++-- | Get electrical bias from the settings.+bias :: Settings -> IO Bias+bias = D.lineSettingsBias++-- | Set drive mode in the settings.+setDrive :: Settings -> Drive -> IO ()+setDrive set driveMode = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDrive set driveMode)++-- | Get drive mode from the settings.+drive :: Settings -> IO Drive+drive = D.lineSettingsDrive++-- | Set event clock source in the settings.+setEventClock :: Settings -> Clock -> IO ()+setEventClock set clk = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEventClock set clk)++-- | Get event clock source from the settings.+eventClock :: Settings -> IO Clock+eventClock = D.lineSettingsEventClock++-- | Set active-low in the settings.+setActiveLow :: Settings -> Bool -> IO ()+setActiveLow = D.lineSettingsSetActiveLow++-- | Get active-low setting.+activeLow :: Settings -> IO Bool+activeLow = D.lineSettingsActiveLow++-- | Set debounce period in microseconds.+setDebouncePeriodUs :: Settings -> Word -> IO ()+setDebouncePeriodUs = D.lineSettingsSetDebouncePeriodUs++-- | Get debounce period in microseconds.+debouncePeriodUs :: Settings -> IO Word+debouncePeriodUs = D.lineSettingsDebouncePeriodUs++-- | Set default output value in the settings.+setOutputValue :: Settings -> Value -> IO ()+setOutputValue _ ValueError = throwIO $ InvalidArgument "setOutputValue: ValueError pattern is a read-only error state and cannot be set as an output value."+setOutputValue set val     = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetOutputValue set val)++-- | Get default output value from the settings.+outputValue :: Settings -> IO Value+outputValue = D.lineSettingsOutputValue++-- | Reset line settings object to default values.+resetSettings :: Settings -> IO ()+resetSettings = D.lineSettingsReset++--------------------------------------------------------------------------------+-- GPIO Line Config+--------------------------------------------------------------------------------++-- | Allocate a new line configuration object and free it automatically afterwards.+-- +withConfig :: (Config -> IO a) -> IO a+withConfig = bracket newLineConfig freeLineConfig++-- | Add settings for a list of line offsets in the configuration.+addSettings :: Config -> [Offset] -> Settings -> IO ()+addSettings config offsets stgs =+  unwrapOrThrow LineConfigNewFailed (D.lineConfigAddLineSettings config (V.fromList offsets) stgs)++-- | Get settings for a specific line offset from configuration.+lineSettings :: Config -> Offset -> IO Settings+lineSettings config offset' = unwrapOrThrow LineConfigNewFailed (D.lineConfigLineSettings config offset')++-- | Set output values for lines in configuration.+setOutputValues :: Config -> [Value] -> IO ()+setOutputValues config vals+  | V.elem ValueError (V.fromList vals) =+    throwIO $ InvalidArgument "setOutputValues: Vector contains ValueError pattern, which cannot be set as an output value."+  | otherwise                          =+    unwrapOrThrow LineConfigNewFailed (D.lineConfigSetOutputValues config (V.fromList vals))++-- | Get the number of configured offsets in the line configuration.+numOffsets :: Config -> IO Word+numOffsets = D.lineConfigNumOffsets++-- | Get all configured line offsets in the configuration as a 'Offset' list.+configuredOffsets :: Config -> IO [Offset]+configuredOffsets config = do+   vec <- D.lineConfigConfiguredOffsets config +   return (V.toList vec)+  ++-- | Reset line configuration object to empty state.+resetConfig :: Config -> IO ()+resetConfig = D.lineConfigReset++--------------------------------------------------------------------------------+-- GPIO Line Request+--------------------------------------------------------------------------------++-- | Request GPIO lines from a chip and automatically release them afterwards.+--+-- Since kernel configuration options ('RequestConfig') are optional,+-- passing 'Nothing' is equivalent to passing a NULL pointer in C.+--+-- @+-- someFunc :: IO ()+-- someFunc = do+--   withRequest chip Nothing config $ \request -> do+--     setLineValue request (Offset 271) Active +-- @+--+-- Assuming offset 271 was configured as output ('DirOutput') with 'setDirection'+-- and added to the configuration with 'addSettings', this drives the physical pin active.+withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a+withRequest chip maybeReqConf lineConf = bracket (requestLines chip maybeReqConf lineConf) releaseLineRequest++-- | Get the logical value of a requested GPIO line at the given offset.+lineValue :: Request -> Offset -> IO Value+lineValue req offset' = unwrapOrThrow LineValueReadFailed (D.lineRequestValue req offset')++-- | Get the logical values of all requested lines as a 'Value' list.+lineValues :: Request -> IO [Value]+lineValues req = do+  vec <- unwrapOrThrow LineValueReadFailed (D.lineRequestValues req)+  return (V.toList vec)++-- | Get the logical values of a subset of requested lines specified by offsets.+lineValuesSubset :: Request -> [Offset] -> IO (V.Vector Value)+lineValuesSubset req offsets =+  unwrapOrThrow LineValueReadFailed (D.lineRequestSubsetValues req (V.fromList offsets))++-- | Set the logical value of a requested GPIO line at the given offset.+setLineValue :: Request -> Offset -> Value -> IO ()+setLineValue _ _ ValueError  = throwIO $ InvalidArgument "setValue: ValueError pattern is a read-only error state and cannot be written to a GPIO line."+setLineValue req offset' val = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValue req offset' val)++-- | Set the logical values of all requested lines from a 'Value' list.+setLineValues :: Request -> [Value] -> IO ()+setLineValues req vals+  | V.elem ValueError (V.fromList vals) =+    throwIO $ InvalidArgument "setValues: Vector contains ValueError pattern, which cannot be written to GPIO lines."+  | otherwise                           =+    unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValues req (V.fromList vals))++-- | Set the logical values of a subset of requested lines from lists of 'Offset' and 'Value'.+setLineValuesSubset :: Request -> [Offset] -> [Value] -> IO ()+setLineValuesSubset req offsets vals+  | V.elem ValueError (V.fromList vals) =+    throwIO $ InvalidArgument "setValuesSubset: Vector contains ValueError pattern, which cannot be written to GPIO lines."+  | otherwise                           =+    unwrapOrThrow LineValueWriteFailed $ D.lineRequestSetValuesSubset req (V.fromList offsets) (V.fromList vals)++-- | Get the name as a 'String' of the chip this request was made on.+chipName :: Request -> IO String+chipName request = do+  name <- D.lineRequestChipName request  +  return (C8.unpack name)++-- | Get the number of lines in the request.+numLines :: Request -> IO Word+numLines = D.lineRequestNumLines++-- | Get all requested line offsets as a 'Offset' list.+requestedOffsets :: Request -> IO [Offset]+requestedOffsets request = do+  vec <- D.lineRequestRequestedOffsets request+  return (V.toList vec)++-- | Get the file descriptor associated with the line request handle.+lineFd :: Request -> IO Fd+lineFd = D.lineRequestFd++-- | Update the configuration of lines associated with an active line request.+reconfigureLines :: Request -> Config -> IO ()+reconfigureLines req config = unwrapOrThrow LineReconfigureFailed (D.lineRequestReconfigure req config)++--------------------------------------------------------------------------------+-- General Utilities+--------------------------------------------------------------------------------++-- | Check if the given filesystem path is a valid GPIO chip character device.+isGPIOChip :: FilePath -> IO Bool+isGPIOChip str = do+  path' <- encodeFS str   +  D.isGPIOChip path' ++-- | Retrieve the underlying libgpiod C API version string (e.g. "2.1").+gpiodAPIVersion :: IO String +gpiodAPIVersion = C8.unpack <$> D.gpiodAPIVersion
− src/Fuyu/GPIO/Chip.hs
@@ -1,66 +0,0 @@--- |--- Module      : Fuyu.GPIO.Chip--- Description : High-level operations for GPIO chips and line watching.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withChip') for opening and closing--- Linux GPIO chips safely, as well as functions for watching line status changes.-module Fuyu.GPIO.Chip-  ( -- * Operations & Brackets-    withChip-  , withChipInfo-  , withLineInfo-  , path-  , offsetFromName-  , fd--    -- * Line Watch & Info Events-  , module Fuyu.GPIO.Chip.Watch--    -- * General Utilities-  , isGPIOChip-  , gpiodAPIVersion-  ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as BS8-import System.Posix.Types (Fd)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Info (withChipInfo)-import Fuyu.GPIO.Line.Info (withLineInfo)-import Fuyu.GPIO.Chip.Unsafe (openChip, closeChip)-import Fuyu.GPIO.Chip.Watch-import Fuyu.GPIO.Exception---- | Open a GPIO chip by filesystem path (e.g. "/dev/gpiochip0") and automatically close it when finished.-withChip :: FilePath -> (Chip -> IO a) -> IO a-withChip path' = bracket (openChip path') closeChip---- | Retrieve chip filesystem path as a 'ByteString'.-path :: Chip -> IO ByteString-path chip = unwrapOrThrow ChipInfoFailed (D.chipPath chip)---- | Map a GPIO line name (e.g. "GPIO17") to its numeric 'Offset' on the chip.-offsetFromName :: Chip -> ByteString -> IO Offset-offsetFromName chip name = unwrapOrThrow LineInfoFailed (D.chipLineOffsetFromName chip name)---- | Get the underlying Linux file descriptor associated with the GPIO chip handle.-fd :: Chip -> IO Fd-fd = D.chipFd------------------------------------------------------------------------------------- General Utilities------------------------------------------------------------------------------------ General Utilities------------------------------------------------------------------------------------- | Check if the given filesystem path is a valid GPIO chip character device.-isGPIOChip :: FilePath -> IO Bool-isGPIOChip = D.isGPIOChip . BS8.pack---- | Retrieve the underlying libgpiod C API version string (e.g. "2.1").-gpiodAPIVersion :: IO ByteString-gpiodAPIVersion = D.gpiodAPIVersion
− src/Fuyu/GPIO/Chip/Info.hs
@@ -1,47 +0,0 @@--- |--- Module      : Fuyu.GPIO.Chip.Info--- Description : Read-only metadata query functions for ChipInfo.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides functions to inspect 'ChipInfo' snapshots.--- It is designed to be imported qualified:------ @--- import qualified Fuyu.GPIO.Chip.Info as ChipInfo--- @-module Fuyu.GPIO.Chip.Info-  ( -- * Types-    ChipInfo--    -- * Managed Resource Allocation-  , withChipInfo--    -- * Metadata Accessors-  , name-  , label-  , numLines-  ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Info.Unsafe (chipInfo, freeChipInfo)-import Fuyu.GPIO.Types---- | Retrieve information about a GPIO chip and free it automatically afterwards.-withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a-withChipInfo chip = bracket (chipInfo chip) freeChipInfo---- | Get the name of the GPIO chip (e.g. "gpiochip4").-name :: ChipInfo -> IO ByteString-name = D.chipInfoName---- | Get the label of the GPIO chip.-label :: ChipInfo -> IO ByteString-label = D.chipInfoLabel---- | Get the total number of lines exposed by the GPIO chip.-numLines :: ChipInfo -> IO Word-numLines = D.chipInfoNumLines
− src/Fuyu/GPIO/Chip/Info/Unsafe.hs
@@ -1,29 +0,0 @@--- |--- Module      : Fuyu.GPIO.Chip.Info.Unsafe--- Description : Unsafe manual resource allocation for ChipInfo.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('chipInfo', 'freeChipInfo') for 'ChipInfo' handles.-module Fuyu.GPIO.Chip.Info.Unsafe-  ( -- * Types-    ChipInfo--    -- * Unsafe Manual Resource Allocation-  , chipInfo-  , freeChipInfo-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | 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
− src/Fuyu/GPIO/Chip/Unsafe.hs
@@ -1,36 +0,0 @@--- |--- Module      : Fuyu.GPIO.Chip.Unsafe--- Description : Unsafe manual resource allocation for GPIO chips and info events.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('openChip', 'closeChip', 'readInfoEvent', 'freeInfoEvent')--- for applications that cannot use managed bracket functions.-module Fuyu.GPIO.Chip.Unsafe-  ( -- * Types-    Chip-  , InfoEvent--    -- * Unsafe Manual Resource Allocation-  , openChip-  , closeChip-  , readInfoEvent-  , freeInfoEvent-  ) where--import qualified Data.ByteString.Char8 as BS8-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Watch.Unsafe (readInfoEvent, freeInfoEvent)-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Open a GPIO chip by its filesystem path (e.g. "/dev/gpiochip4").--- Must be manually closed using 'closeChip'.-openChip :: FilePath -> IO Chip-openChip path = unwrapOrThrow (ChipOpenFailed path) (D.chipOpen (BS8.pack path))---- | Close a GPIO chip handle.-closeChip :: Chip -> IO ()-closeChip = D.chipClose-
− src/Fuyu/GPIO/Chip/Watch.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE PatternSynonyms #-}---- |--- Module      : Fuyu.GPIO.Chip.Watch--- Description : Operations for watching GPIO line status events.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withWatchLine', 'withEvent')--- and functions for watching line status changes (e.g. requested, released, reconfigured).------ It is designed to be imported qualified or used via top-level "Fuyu.GPIO":------ @--- import qualified Fuyu.GPIO.Chip.Watch as Watch--- @-module Fuyu.GPIO.Chip.Watch-  ( -- * Types & Patterns-    Chip-  , ReadyChip(..)-  , readyToChip-  , WaitResult(..)-  , LineInfo-  , Offset-  , pattern Offset-  , Timeout-  , pattern Nanoseconds-  , pattern Immediate-  , pattern Infinite-  , Timestamp-  , InfoEvent-  , InfoEventType-  , pattern Requested-  , pattern Released-  , pattern ConfigChanged--    -- * Managed Brackets-  , withWatchLine-  , withEvent--    -- * Line Watch Operations-  , watchLine-  , unwatchLine-  , waitEvent--    -- * InfoEvent Accessors-  , eventType-  , timestampNs-  , lineInfo-  ) where--import Control.Exception (bracket)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Watch.Unsafe (readInfoEvent, freeInfoEvent)-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types hiding (eventType)---- | Start watching a line for status change events (e.g. requested, released, reconfigured)--- within a bracket, automatically unwatching the line 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') (\_ -> 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 <- unwrapOrThrow WaitInfoEventFailed (D.chipWaitInfoEvent chip timeout)-  pure $ case res of-    D.EventReady -> EventReady (ReadyChip chip)-    D.Timeout    -> TimeoutResult---- | Read a status change info event from a chip once 'waitEvent' indicates it is ready,--- and automatically free it afterwards.-withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a-withEvent readyChip = bracket (readInfoEvent readyChip) freeInfoEvent---- | Get the event type of an 'InfoEvent' ('Requested', 'Released', 'ConfigChanged').-eventType :: InfoEvent -> IO InfoEventType-eventType = D.infoEventType---- | Get the timestamp in nanoseconds of an 'InfoEvent'.-timestampNs :: InfoEvent -> IO Timestamp-timestampNs = D.infoEventTimestamp---- | Get the line info snapshot associated with an 'InfoEvent'.-lineInfo :: InfoEvent -> IO LineInfo-lineInfo = D.infoEventLineInfo
− src/Fuyu/GPIO/Chip/Watch/Unsafe.hs
@@ -1,33 +0,0 @@--- |--- Module      : Fuyu.GPIO.Chip.Watch.Unsafe--- Description : Unsafe manual resource allocation for line info events.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('readInfoEvent', 'freeInfoEvent')--- for applications that cannot use managed bracket functions.-module Fuyu.GPIO.Chip.Watch.Unsafe-  ( -- * Types & Security Token-    Chip-  , ReadyChip(..)-  , readyToChip-  , InfoEvent--    -- * Unsafe Manual Resource Allocation-  , readInfoEvent-  , freeInfoEvent-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Read a line info event from a chip after 'Fuyu.GPIO.Chip.Watch.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
src/Fuyu/GPIO/EdgeEvent.hs view
@@ -10,8 +10,14 @@ -- This module provides managed resource brackets ('withBuffer') and functions for waiting -- on edge events ('waitEvents') and reading them ('readEvents') securely using the -- 'ReadyRequest' capability token.+--+-- It is designed to be imported qualified:+--+-- @+-- import qualified Fuyu.GPIO.EdgeEvent as Edge+-- @ module Fuyu.GPIO.EdgeEvent-  ( -- * Security Token & Wait Result+  ( -- * Security Tokens & Wait Result     WaitResult(..)   , ReadyRequest(..)   , readyToRequest@@ -21,8 +27,10 @@   , Capacity   , userBufferCapacity   , capacity-  , Event+  , RawEvent   , Timeout+  , Event+  , EventType     , pattern Nanoseconds   , pattern Immediate   , pattern Infinite@@ -34,18 +42,19 @@     -- * Event Data Type & Parser   , NonEmpty(..)   , EdgeEvent(..)-  , parseEvent+  , parseRawEvent -    -- * Event Buffer Operations (Managed)+    -- * Event Buffer Operations   , withBuffer   , bufferCapacity   , bufferNumEvents-  , bufferEvent+  , bufferRawEvent      -- * Waiting & Reading Events   , waitEvents   , readEvents-  , withRawEvents+  , forRawEvents+  , forRawEvents_      -- * RawEdgeEvent Metadata Accessors   , eventType@@ -56,16 +65,30 @@   , copyEvent   ) where -import Control.Exception (bracket)-import Control.Monad (forM)+import Control.Exception (bracket, throwIO)+import Foreign.C.Error (Errno(..))+import Control.Monad (forM, forM_) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE import Data.Word (Word64) import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.EdgeEvent.Unsafe (newEventBuffer, freeEventBuffer, readEventsRaw)+import Fuyu.GPIO.Unsafe (newEventBuffer, freeEventBuffer, readEventsRaw) import Fuyu.GPIO.Exception import Fuyu.GPIO.Types hiding (eventType) +--------------------------------------------------------------------------------+-- Domain Type Aliases+--------------------------------------------------------------------------------+-- | Type alias for 'EdgeEvent' designed for qualified use (e.g. @Edge.Event@).+type Event = EdgeEvent++-- | Type alias for 'EdgeEventType' designed for qualified use (e.g. @Edge.EventType@).+type EventType = EdgeEventType   ++--------------------------------------------------------------------------------+-- Core Edge Event Functions  +--------------------------------------------------------------------------------+  -- | Allocate an edge event buffer of the specified capacity and free it automatically afterwards. withBuffer :: Capacity -> (Buffer -> IO a) -> IO a withBuffer capacity' = bracket (newEventBuffer capacity') freeEventBuffer@@ -78,67 +101,79 @@ bufferNumEvents :: Buffer -> IO Word bufferNumEvents = D.eventBufferNumEvents --- | Get a specific edge event from the buffer by index.-bufferEvent :: Buffer -> Word -> IO Event-bufferEvent buf idx = unwrapOrThrow ReadEdgeEventsFailed (D.eventBufferGetEvent buf idx)+-- | Get a specific edge event from the buffer by index, returning 'Nothing' if the index is out of bounds.+bufferRawEvent :: Buffer -> Word -> IO (Maybe RawEvent)+bufferRawEvent buf idx = do+  res <- D.eventBufferGetEvent buf idx+  pure $ case res of+    Right ev -> Just ev+    Left _   -> Nothing  -- | Wait for edge events to occur on requested lines until the specified timeout. -- Throws 'WaitEdgeEventsFailed' on error. waitEvents :: Request -> Timeout -> IO (WaitResult ReadyRequest) waitEvents req timeout = do-  res <- unwrapOrThrow WaitEdgeEventsFailed (D.lineRequestWaitEdgeEvents req timeout)-  pure $ case res of-    D.EventReady -> EventReady (ReadyRequest req)-    D.Timeout    -> TimeoutResult+  res <- D.lineRequestWaitEdgeEvents req timeout+  case res of+    Left (Errno 4)     -> waitEvents req timeout  -- Retry on EINTR so GHC RTS can deliver UserInterrupt.+    Left err           -> throwIO (WaitEdgeEventsFailed err)+    Right D.EventReady -> pure (EventReady (ReadyRequest req))+    Right D.Timeout    -> pure TimeoutResult  -- | Parse a raw edge event pointer into a pure Haskell 'EdgeEvent' structure.-parseEvent :: Event -> IO EdgeEvent-parseEvent ev = EdgeEvent+parseRawEvent :: RawEvent -> IO Event+parseRawEvent ev = EdgeEvent   <$> D.rawEdgeEventLineOffset ev   <*> D.rawEdgeEventType ev   <*> D.rawEdgeEventTimestampNs ev  -- | Read buffered edge events once 'waitEvents' indicates they are ready, -- parsing them into a non-empty list of pure 'EdgeEvent' structures.-readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty EdgeEvent)-readEvents readyReq buf = withRawEvents readyReq buf parseEvent+readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty Event)+readEvents readyReq buf = forRawEvents readyReq buf parseRawEvent  -- | Process raw edge events directly in the buffer using a callback without intermediate allocations, -- returning a non-empty list of results.-withRawEvents :: ReadyRequest -> Buffer -> (Event -> IO a) -> IO (NonEmpty a)-withRawEvents readyReq buf action = do+forRawEvents :: ReadyRequest -> Buffer -> (RawEvent -> IO a) -> IO (NonEmpty a)+forRawEvents readyReq buf action = do   count <- readEventsRaw readyReq buf   results <- forM [0 .. count - 1] $ \idx -> do-    ev <- bufferEvent buf (fromIntegral idx)+    Just ev <- bufferRawEvent buf (fromIntegral idx)+    action ev  +  return $ NE.fromList results+  +-- | Same as 'forRawEvents' but ignore the results +forRawEvents_ :: ReadyRequest -> Buffer -> (RawEvent -> IO b) -> IO ()+forRawEvents_ readyReq buf action = do+  count <- readEventsRaw readyReq buf+  forM_ [0 .. count - 1] $ \idx -> do+    Just ev <- bufferRawEvent buf (fromIntegral idx)     action ev-  case NE.nonEmpty results of-    Just ne -> pure ne-    Nothing -> ioError (userError "readEvents: expected at least one event from ReadyRequest but got none")-+   ----------------------------------------------------------------------------------- RawEdgeEvent Metadata Accessors+-- RawEvent Metadata Accessors --------------------------------------------------------------------------------  -- | Get the type of event ('Rising' or 'Falling').-eventType :: Event -> IO EdgeEventType+eventType :: RawEvent -> IO EventType eventType = D.rawEdgeEventType  -- | Get the event timestamp in nanoseconds.-timestampNs :: Event -> IO Timestamp+timestampNs :: RawEvent -> IO Timestamp timestampNs = D.rawEdgeEventTimestampNs  -- | Get the offset of the line that triggered the event.-lineOffset :: Event -> IO Offset+lineOffset :: RawEvent -> IO Offset lineOffset = D.rawEdgeEventLineOffset  -- | Get the global sequence number of the event.-globalSeqNo :: Event -> IO Word64+globalSeqNo :: RawEvent -> IO Word64 globalSeqNo = D.rawEdgeEventGlobalSeqNo  -- | Get the line-specific sequence number of the event.-lineSeqNo :: Event -> IO Offset+lineSeqNo :: RawEvent -> IO Offset lineSeqNo = D.rawEdgeEventLineSeqNo  -- | Make a copy of a raw edge event object.-copyEvent :: Event -> IO Event+copyEvent :: RawEvent -> IO RawEvent copyEvent ev = unwrapOrThrow RawEdgeEventCopyFailed (D.rawEdgeEventCopy ev)
− src/Fuyu/GPIO/EdgeEvent/Unsafe.hs
@@ -1,42 +0,0 @@--- |--- Module      : Fuyu.GPIO.EdgeEvent.Unsafe--- Description : Unsafe manual resource allocation and raw buffer reading for event buffers.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newEventBuffer', 'freeEventBuffer') and raw reading ('readEventsRaw') for edge 'Buffer' handles.-module Fuyu.GPIO.EdgeEvent.Unsafe-  ( -- * Types & Security Token-    Buffer-  , Capacity-  , userBufferCapacity-  , capacity-  , ReadyRequest(..)--    -- * Unsafe Manual Resource Allocation & Reading-  , newEventBuffer-  , freeEventBuffer-  , readEventsRaw-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | 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)
src/Fuyu/GPIO/Exception.hs view
@@ -1,28 +1,25 @@ {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE ScopedTypeVariables #-}  -- | -- Module      : Fuyu.GPIO.Exception--- Description : Exception types and high-level application handler for fuyu-gpio operations.+-- Description : Exception types for fuyu-gpio operations. -- Maintainer  : BassGT -- Stability   : experimental -- Portability : POSIX ----- High-level exception type 'GpioException' thrown by fuyu-gpio operations,--- and managed application runner 'withGpioApp' for graceful signal handling.+-- High-level exception type 'GpioException' thrown by fuyu-gpio operations. module Fuyu.GPIO.Exception   ( GpioException(..)   , unwrapOrThrow-  , withGpioApp   ) where -import Control.Exception (Exception, SomeException, catch, fromException, throwIO, AsyncException(UserInterrupt))+import Control.Exception (Exception, throwIO)+import System.OsPath.Posix (PosixPath) import Foreign.C.Error (Errno(..))-import Control.Monad (void)  -- | High-level exceptions thrown by fuyu-gpio operations. data GpioException-  = ChipOpenFailed FilePath Errno+  = ChipOpenFailed PosixPath Errno   | ChipInfoFailed Errno   | LineInfoFailed Errno   | LineSettingsNewFailed Errno@@ -42,33 +39,10 @@   | LineInfoCopyFailed Errno   | CustomGpioError String Errno   | InvalidArgument String-  deriving (Exception)--instance Eq GpioException where-  ChipOpenFailed p1 e1 == ChipOpenFailed p2 e2 = p1 == p2 && e1 == e2-  ChipInfoFailed e1 == ChipInfoFailed e2 = e1 == e2-  LineInfoFailed e1 == LineInfoFailed e2 = e1 == e2-  LineSettingsNewFailed e1 == LineSettingsNewFailed e2 = e1 == e2-  LineSettingsSetFailed e1 == LineSettingsSetFailed e2 = e1 == e2-  LineConfigNewFailed e1 == LineConfigNewFailed e2 = e1 == e2-  RequestConfigNewFailed e1 == RequestConfigNewFailed e2 = e1 == e2-  LineRequestFailed e1 == LineRequestFailed e2 = e1 == e2-  EventBufferNewFailed e1 == EventBufferNewFailed e2 = e1 == e2-  LineValueReadFailed e1 == LineValueReadFailed e2 = e1 == e2-  LineValueWriteFailed e1 == LineValueWriteFailed e2 = e1 == e2-  LineReconfigureFailed e1 == LineReconfigureFailed e2 = e1 == e2-  WaitEdgeEventsFailed e1 == WaitEdgeEventsFailed e2 = e1 == e2-  ReadEdgeEventsFailed e1 == ReadEdgeEventsFailed e2 = e1 == e2-  WaitInfoEventFailed e1 == WaitInfoEventFailed e2 = e1 == e2-  ReadInfoEventFailed e1 == ReadInfoEventFailed e2 = e1 == e2-  RawEdgeEventCopyFailed e1 == RawEdgeEventCopyFailed e2 = e1 == e2-  LineInfoCopyFailed e1 == LineInfoCopyFailed e2 = e1 == e2-  CustomGpioError s1 e1 == CustomGpioError s2 e2 = s1 == s2 && e1 == e2-  InvalidArgument msg1 == InvalidArgument msg2 = msg1 == msg2-  _ == _ = False+  deriving (Eq, Exception)  instance Show GpioException where-  show (ChipOpenFailed path (Errno e)) = "ChipOpenFailed: Failed to open chip at '" ++ path ++ "' (errno " ++ show e ++ ")"+  show (ChipOpenFailed path (Errno e)) = "ChipOpenFailed: Failed to open chip at " ++ show path ++ " (errno " ++ show e ++ ")"   show (ChipInfoFailed (Errno e)) = "ChipInfoFailed (errno " ++ show e ++ ")"   show (LineInfoFailed (Errno e)) = "LineInfoFailed (errno " ++ show e ++ ")"   show (LineSettingsNewFailed (Errno e)) = "LineSettingsNewFailed (errno " ++ show e ++ ")"@@ -97,20 +71,3 @@     Left errno -> throwIO (mkExc errno)     Right val  -> pure val --- | High-level managed application runner.--- Automatically handles 'Ctrl+C' ('UserInterrupt'), interrupted system calls ('EINTR' / 'WaitEdgeEventsFailed'),--- and prints formatted 'GpioException' messages cleanly without uncaught backtraces.-withGpioApp :: IO a -> IO ()-withGpioApp action = void action `catch` handleAppException-  where-    handleAppException :: SomeException -> IO ()-    handleAppException exc-      | isUserInterrupt exc = putStrLn "\nLoop terminated successfully!"-      | Just (WaitEdgeEventsFailed (Errno 4)) <- fromException exc = putStrLn "\nLoop terminated successfully!"-      | Just (gpioErr :: GpioException) <- fromException exc = putStrLn $ "\n[GPIO Exception]: " ++ show gpioErr-      | otherwise = throwIO exc--    isUserInterrupt :: SomeException -> Bool-    isUserInterrupt e = case fromException e of-      Just UserInterrupt -> True-      _                  -> False
− src/Fuyu/GPIO/Line.hs
@@ -1,279 +0,0 @@--- |--- Module      : Fuyu.GPIO.Line--- Description : High-level operations for GPIO line settings, requests, and value I/O.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withSettings', 'withConfig', 'withRequest')--- for configuring GPIO line properties (direction, bias, drive mode, active-low, debounce)--- and requesting access to read or write logical values to GPIO lines.-module Fuyu.GPIO.Line-  ( -- * Types & Patterns-    Settings-  , Config-  , Request-  , RequestConfig-  , Offset-  , pattern Offset-  , Value-  , pattern Active-  , pattern Inactive-  , pattern ValueError-  , 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--    -- * Managed Resource Allocation (with*)-  , withSettings-  , withConfig-  , withRequest--    -- * Line Settings Operations-  , setDirection-  , direction-  , setEdgeDetection-  , edgeDetection-  , setBias-  , bias-  , setDrive-  , drive-  , setEventClock-  , eventClock-  , setActiveLow-  , activeLow-  , setDebouncePeriodUs-  , debouncePeriodUs-  , setOutputValue-  , outputValue-  , resetSettings--    -- * Line Configuration Operations-  , addSettings-  , settings-  , setOutputValues-  , numOffsets-  , configuredOffsets-  , resetConfig--    -- * Line Value Operations (Read / Write)-  , value-  , values-  , valuesSubset-  , setValue-  , setValues-  , setValuesSubset--    -- * Line Request Operations & Metadata-  , chipName-  , numLines-  , requestedOffsets-  , fd-  , reconfigureLines-  ) where--import Control.Exception (bracket, throwIO)-import Data.ByteString (ByteString)-import qualified Data.Vector.Storable as V-import System.Posix.Types (Fd)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Line.Unsafe (newSettings, freeSettings, newConfig, freeConfig, requestLines, releaseRequest)-import Fuyu.GPIO.Types------------------------------------------------------------------------------------- Resource Bracket Management------------------------------------------------------------------------------------- | Allocate a new line settings object and free it automatically afterwards.-withSettings :: (Settings -> IO a) -> IO a-withSettings = bracket newSettings freeSettings---- | Allocate a new line configuration object and free it automatically afterwards.-withConfig :: (Config -> IO a) -> IO a-withConfig = bracket newConfig freeConfig---- | Request GPIO lines from a chip and automatically release them afterwards.-withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a-withRequest chip maybeReqConf lineConf = bracket (requestLines chip maybeReqConf lineConf) releaseRequest------------------------------------------------------------------------------------- Line Settings Setters & Getters------------------------------------------------------------------------------------- | Set the line direction in the settings.-setDirection :: Settings -> Direction -> IO ()-setDirection set dir = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDirection set dir)---- | Get the line direction from the settings.-direction :: Settings -> IO Direction-direction = D.lineSettingsDirection---- | Set edge detection in the settings.-setEdgeDetection :: Settings -> Edge -> IO ()-setEdgeDetection set edge = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEdgeDetection set edge)---- | Get edge detection from the settings.-edgeDetection :: Settings -> IO Edge-edgeDetection = D.lineSettingsEdgeDetection---- | Set electrical bias in the settings.-setBias :: Settings -> Bias -> IO ()-setBias _ BiasUnknown = throwIO $ InvalidArgument "setBias: BiasUnknown is a read-only state and cannot be set as a bias configuration."-setBias set biasVal      = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetBias set biasVal)---- | Get electrical bias from the settings.-bias :: Settings -> IO Bias-bias = D.lineSettingsBias---- | Set drive mode in the settings.-setDrive :: Settings -> Drive -> IO ()-setDrive set driveMode = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDrive set driveMode)---- | Get drive mode from the settings.-drive :: Settings -> IO Drive-drive = D.lineSettingsDrive---- | Set event clock source in the settings.-setEventClock :: Settings -> Clock -> IO ()-setEventClock set clk = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEventClock set clk)---- | Get event clock source from the settings.-eventClock :: Settings -> IO Clock-eventClock = D.lineSettingsEventClock---- | Set active-low in the settings.-setActiveLow :: Settings -> Bool -> IO ()-setActiveLow = D.lineSettingsSetActiveLow---- | Get active-low setting.-activeLow :: Settings -> IO Bool-activeLow = D.lineSettingsActiveLow---- | Set debounce period in microseconds.-setDebouncePeriodUs :: Settings -> Word -> IO ()-setDebouncePeriodUs = D.lineSettingsSetDebouncePeriodUs---- | Get debounce period in microseconds.-debouncePeriodUs :: Settings -> IO Word-debouncePeriodUs = D.lineSettingsDebouncePeriodUs---- | Set default output value in the settings.-setOutputValue :: Settings -> Value -> IO ()-setOutputValue _ ValueError = throwIO $ InvalidArgument "setOutputValue: ValueError pattern is a read-only error state and cannot be set as an output value."-setOutputValue set val = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetOutputValue set val)---- | Get default output value from the settings.-outputValue :: Settings -> IO Value-outputValue = D.lineSettingsOutputValue---- | Reset line settings object to default values.-resetSettings :: Settings -> IO ()-resetSettings = D.lineSettingsReset------------------------------------------------------------------------------------- Line Configuration Operations------------------------------------------------------------------------------------- | Add settings for a vector of line offsets in the configuration.-addSettings :: Config -> V.Vector Offset -> Settings -> IO ()-addSettings config offsets stgs = unwrapOrThrow LineConfigNewFailed (D.lineConfigAddLineSettings config offsets stgs)---- | Get settings for a specific line offset from configuration.-settings :: Config -> Offset -> IO Settings-settings config offset' = unwrapOrThrow LineConfigNewFailed (D.lineConfigLineSettings config offset')---- | Set output values for lines in configuration.-setOutputValues :: Config -> V.Vector Value -> IO ()-setOutputValues config vals-  | V.elem ValueError vals = throwIO $ InvalidArgument "setOutputValues: Vector contains ValueError pattern, which cannot be set as an output value."-  | otherwise              = unwrapOrThrow LineConfigNewFailed (D.lineConfigSetOutputValues config vals)---- | Get the number of configured offsets in the line configuration.-numOffsets :: Config -> IO Word-numOffsets = D.lineConfigNumOffsets---- | Get all configured line offsets in the configuration as a Storable 'V.Vector'.-configuredOffsets :: Config -> IO (V.Vector Offset)-configuredOffsets = D.lineConfigConfiguredOffsets---- | Reset line configuration object to empty state.-resetConfig :: Config -> IO ()-resetConfig = D.lineConfigReset------------------------------------------------------------------------------------- Line Reading & Writing------------------------------------------------------------------------------------- | Get the logical value of a requested GPIO line at the given offset.-value :: Request -> Offset -> IO Value-value req offset' = unwrapOrThrow LineValueReadFailed (D.lineRequestValue req offset')---- | Get the logical values of all requested lines as a Storable 'V.Vector'.-values :: Request -> IO (V.Vector Value)-values req = unwrapOrThrow LineValueReadFailed (D.lineRequestValues req)---- | Get the logical values of a subset of requested lines specified by offsets.-valuesSubset :: Request -> V.Vector Offset -> IO (V.Vector Value)-valuesSubset req offsets = unwrapOrThrow LineValueReadFailed (D.lineRequestSubsetValues req offsets)---- | Set the logical value of a requested GPIO line at the given offset.-setValue :: Request -> Offset -> Value -> IO ()-setValue _ _ ValueError  = throwIO $ InvalidArgument "setValue: ValueError pattern is a read-only error state and cannot be written to a GPIO line."-setValue req offset' val = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValue req offset' val)---- | Set the logical values of all requested lines from a Storable 'V.Vector'.-setValues :: Request -> V.Vector Value -> IO ()-setValues req vals-  | V.elem ValueError vals = throwIO $ InvalidArgument "setValues: Vector contains ValueError pattern, which cannot be written to GPIO lines."-  | otherwise              = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValues req vals)---- | Set the logical values of a subset of requested lines from vectors of offsets and values.-setValuesSubset :: Request -> V.Vector Offset -> V.Vector Value -> IO ()-setValuesSubset req offsets vals-  | V.elem ValueError vals = throwIO $ InvalidArgument "setValuesSubset: Vector contains ValueError pattern, which cannot be written to GPIO lines."-  | otherwise              = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValuesSubset req offsets vals)------------------------------------------------------------------------------------- Line Request Operations & Metadata------------------------------------------------------------------------------------- | Get the name of the chip this request was made on.-chipName :: Request -> IO ByteString-chipName = D.lineRequestChipName---- | Get the number of lines in the request.-numLines :: Request -> IO Word-numLines = D.lineRequestNumLines---- | Get all requested line offsets as a Storable 'V.Vector'.-requestedOffsets :: Request -> IO (V.Vector Offset)-requestedOffsets = D.lineRequestRequestedOffsets---- | Get the file descriptor associated with the line request handle.-fd :: Request -> IO Fd-fd = D.lineRequestFd---- | Update the configuration of lines associated with an active line request.-reconfigureLines :: Request -> Config -> IO ()-reconfigureLines req config = unwrapOrThrow LineReconfigureFailed (D.lineRequestReconfigure req config)
− src/Fuyu/GPIO/Line/Info.hs
@@ -1,92 +0,0 @@--- |--- Module      : Fuyu.GPIO.Line.Info--- Description : Read-only metadata query functions for LineInfo snapshots.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides functions to inspect 'LineInfo' snapshots.--- It is designed to be imported qualified:------ @--- import qualified Fuyu.GPIO.Line.Info as LineInfo--- @-module Fuyu.GPIO.Line.Info-  ( -- * Types-    LineInfo--    -- * Managed Resource Allocation-  , withLineInfo--    -- * Metadata Accessors-  , offset-  , name-  , isUsed-  , consumer-  , direction-  , edgeDetection-  , bias-  , drive-  , isActiveLow-  , isDebounced-  , debouncePeriod-  , eventClock-  ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Line.Info.Unsafe (lineInfo, freeLineInfo)-import Fuyu.GPIO.Types---- | 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 (lineInfo chip offset') 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.-name :: LineInfo -> IO (Maybe ByteString)-name = D.lineInfoName---- | 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 ByteString)-consumer = D.lineInfoConsumer---- | 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
− src/Fuyu/GPIO/Line/Info/Unsafe.hs
@@ -1,35 +0,0 @@--- |--- Module      : Fuyu.GPIO.Line.Info.Unsafe--- Description : Unsafe manual resource allocation for LineInfo snapshots.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('lineInfo', 'freeLineInfo', 'copyLineInfo') for 'LineInfo' handles.-module Fuyu.GPIO.Line.Info.Unsafe-  ( -- * Types-    LineInfo--    -- * Unsafe Manual Resource Allocation-  , lineInfo-  , freeLineInfo-  , copyLineInfo-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | 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)
− src/Fuyu/GPIO/Line/Unsafe.hs
@@ -1,57 +0,0 @@--- |--- Module      : Fuyu.GPIO.Line.Unsafe--- Description : Unsafe manual resource allocation for line settings, config, and requests.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newSettings', 'freeSettings', 'newConfig', 'freeConfig',--- 'requestLines', 'releaseRequest') for line settings, configs, and requests.-module Fuyu.GPIO.Line.Unsafe-  ( -- * Types-    Chip-  , Settings-  , Config-  , Request-  , RequestConfig--    -- * Unsafe Manual Resource Allocation-  , newSettings-  , freeSettings-  , newConfig-  , freeConfig-  , requestLines-  , releaseRequest-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Allocate a new line settings object.--- Must be manually freed with 'freeSettings'.-newSettings :: IO Settings-newSettings = unwrapOrThrow LineSettingsNewFailed D.lineSettingsNew---- | Free a line settings object.-freeSettings :: Settings -> IO ()-freeSettings = D.lineSettingsFree---- | Allocate a new line configuration object.--- Must be manually freed with 'freeConfig'.-newConfig :: IO Config-newConfig = unwrapOrThrow LineConfigNewFailed D.lineConfigNew---- | Free a line configuration object.-freeConfig :: Config -> IO ()-freeConfig = D.lineConfigFree---- | Request GPIO lines from a chip.--- Must be manually released with 'releaseRequest'.-requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request-requestLines chip maybeReqConf lineConf =-  unwrapOrThrow LineRequestFailed (D.chipRequestLines chip maybeReqConf lineConf)---- | Release a line request handle.-releaseRequest :: Request -> IO ()-releaseRequest = D.lineRequestRelease
+ src/Fuyu/GPIO/Monitor.hs view
@@ -0,0 +1,239 @@+{-# 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
src/Fuyu/GPIO/RequestConfig.hs view
@@ -19,7 +19,7 @@ import Control.Exception (bracket) import Data.ByteString (ByteString) import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.RequestConfig.Unsafe (newRequestConfig, freeRequestConfig)+import Fuyu.GPIO.Unsafe (newRequestConfig, freeRequestConfig) import Fuyu.GPIO.Types  -- | Allocate a new request configuration object and free it automatically afterwards.
− src/Fuyu/GPIO/RequestConfig/Unsafe.hs
@@ -1,29 +0,0 @@--- |--- Module      : Fuyu.GPIO.RequestConfig.Unsafe--- Description : Unsafe manual resource allocation for RequestConfig.--- Maintainer  : BassGT--- Stability   : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newRequestConfig', 'freeRequestConfig') for 'RequestConfig' handles.-module Fuyu.GPIO.RequestConfig.Unsafe-  ( -- * Types-    RequestConfig--    -- * Unsafe Manual Resource Allocation-  , newRequestConfig-  , freeRequestConfig-  ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | 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
src/Fuyu/GPIO/Types.hs view
@@ -9,7 +9,7 @@   , Request   , RequestConfig   , Buffer-  , Event+  , RawEvent   , ReadyRequest(..)   , readyToRequest   , ReadyChip(..)@@ -102,7 +102,7 @@ -- | Alias for 'D.EventBuffer' type Buffer        = D.EventBuffer -- | Alias for 'D.RawEdgeEvent'-type Event         = D.RawEdgeEvent+type RawEvent      = D.RawEdgeEvent  -- | Alias for 'D.LineOffset' type Offset        = D.LineOffset
+ src/Fuyu/GPIO/Unsafe.hs view
@@ -0,0 +1,241 @@+-- |+-- 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)